Skrypt Bash: podziel słowo na każdą literę


17

Jak mogę rozdzielić litery słowa, a każdą literę w osobnym wierszu?

Na przykład, biorąc pod uwagę "StackOver" , że chciałbym zobaczyć

S
t
a
c
k
O
v
e
r

Jestem nowy w bash, więc nie mam pojęcia, od czego zacząć.

Odpowiedzi:


29

Użyłbym grep:

$ grep -o . <<<"StackOver"
S
t
a
c
k
O
v
e
r

lub sed:

$ sed 's/./&\n/g' <<<"StackOver"
S
t
a
c
k
O
v
e
r

A jeśli problem stanowi puste miejsce na końcu:

sed 's/\B/&\n/g' <<<"StackOver"

Wszystko to przy założeniu GNU / Linux.


grep -o. <<< ¿¿¿.. -o wyszukuje WZÓR podany, prawda? i co on tu robi w twoim rozkazie?
Sijaan Hallak

1
@jimmij Nie mogę znaleźć żadnej pomocy dotyczącej tego, co naprawdę <<< robi! jakaś pomoc?
Sijaan Hallak

3
@SijaanHallak Jest to tak zwany Here string, grosso modo odpowiednik po echo foo | ...prostu mniej pisania. Zobacz tldp.org/LDP/abs/html/x17837.html
jimmij

1
@SijaanHallak zmień .na \B(nie pasuje do granicy słów).
jimmij

1
@ SijaanHallak - możesz upuścić drugi sedjak:sed -et -e's/./\n&/g;//D'
mikeserv

19

Jeśli chcesz drukować tekst w pionie, możesz chcieć przełamać klastry grafemów zamiast znaków. Na przykład z eostrym akcentem:

  • W przypadku klastrów grafemowych ( ez ostrym akcentem byłby jeden klaster grafemowy):

    $ perl -CLAS -le 'for (@ARGV) {print for /\X/g}' $'Ste\u301phane'
    S
    t
    é
    p
    h
    a
    n
    e
    

    (lub grep -Po '\X'z GNU grep zbudowanym z obsługą PCRE)

  • Ze znakami (tutaj z GNU grep):

    $ printf '%s\n' $'Ste\u301phane' | grep -o .
    S
    t
    e
    
    p
    h
    a
    n
    e
    
  • foldma łamać znaki, ale GNU foldnie obsługuje znaków wielobajtowych, więc zamiast tego łamie bajty:

    $ printf '%s\n' $'Ste\u301phane' | fold -w 1
    S
    t
    e
    �
    �
    p
    h
    a
    n
    e
    

Na StackOver, który składa się tylko ze znaków ASCII (więc jeden bajt na znak, jeden znak na klaster grafemu), wszystkie trzy dają ten sam wynik.


Jestem zaskoczony, grep -Poże nie robi tego, czego można by się spodziewać (jak grep -Probi).
jimmij

@jimmij, co masz na myśli? grep -Po .znajduje znaki (a łączący ostry akcent po znaku nowej linii jest nieprawidłowy) i grep -Po '\X'znajduje dla mnie klastry grafhem. Może być potrzebna najnowsza wersja grep i / lub PCRE, aby działała poprawnie (lub spróbuj grep -Po '(*UTF8)\X')
Stéphane Chazelas


6

Jeśli masz perl6 w swoim pudełku:

$ perl6 -e 'for @*ARGS -> $w { .say for $w.comb }' 'cường'       
c
ư
ờ
n
g

pracować niezależnie od lokalizacji.


6

Z wieloma awkwersjami

awk -F '' -v OFS='\n' '{$1=$1};1' <<<'StackOver'

Świetny! Ale w mojej wersji nAWK („One True AWK”) to nie działa. Jednak to załatwia sprawę: awk -v FS='' -v OFS='\n' '{$1=$1};1' (zastanawiając się, czy to jest bardziej mobilny, ponieważ -F ''może uzyskując ERE: //)
eruve

4

Poniżej będą ogólne:

$ awk -F '' \
   'BEGIN { RS = ""; OFS = "\n"} {for (i=1;i<=NF;i++) $i = $i; print }' <file_name>


4

Ponieważ konkretnie poprosiłeś o odpowiedź w bash, oto sposób na zrobienie tego w czystym bashu:

while read -rn1; do echo "$REPLY" ; done <<< "StackOver"

Pamiętaj, że spowoduje to złapanie nowego wiersza na końcu „ dokumentu tutaj ”. Jeśli chcesz tego uniknąć, ale nadal iterować znaki za pomocą pętli bash, użyj, printfaby uniknąć nowej linii.

printf StackOver | while read -rn1; do echo "$REPLY" ; done

4

Również Python 2 może być używany z wiersza poleceń:

python <<< "for x in 'StackOver':
   print x"

lub:

echo "for x in 'StackOver':
    print x" | python

lub (jak skomentował 1_CR) w Pythonie 3 :

python3 -c "print(*'StackOver',sep='\n')"

4

Możesz użyć fold (1)polecenia. Jest bardziej wydajny niż grepi sed.

$ time grep -o . <bigfile >/dev/null

real    0m3.868s
user    0m3.784s
sys     0m0.056s
$ time fold -b1 <bigfile >/dev/null

real    0m0.555s
user    0m0.528s
sys     0m0.016s
$

Jedną znaczącą różnicą jest to, że fold odtworzy puste linie na wyjściu:

$ grep -o . <(printf "A\nB\n\nC\n\n\nD\n")
A
B
C
D
$ fold -b1 <(printf "A\nB\n\nC\n\n\nD\n")
A
B

C


D
$ 

3

Możesz obsługiwać znaki wielobajtowe, takie jak:

<input \
dd cbs=1 obs=2 conv=unblock |
sed -e:c -e '/^.*$/!N;s/\n//;tc'

Co może być bardzo przydatne, gdy pracujesz z danymi wejściowymi na żywo, ponieważ nie ma tam buforowania, a postać jest drukowana, gdy tylko będzie cała .


NP, czy powinniśmy dodać notatkę o lokalizacji?
cuonglm

Nie działa w przypadku łączenia postaci takich jak odpowiedź Stéphane Chazelas, ale przy odpowiedniej normalizacji nie powinno to mieć znaczenia.
Kay jest rozczarowany w SE

@Kay - służy do łączenia znaków, jeśli chcesz - do tego właśnie sedsłużą skrypty. raczej nie napiszę o tym teraz - jestem dość śpiący. jest to jednak bardzo przydatne podczas czytania terminala.
mikeserv

@cuonglm - jeśli chcesz. powinien jednak działać tylko dla ustawień regionalnych, biorąc pod uwagę rozsądną libc.
mikeserv

Zauważ, że ddspowoduje to przerwanie znaków wielobajtowych, więc wynik nie będzie już tekstem, więc zachowanie sed będzie nieokreślone zgodnie z POSIX.
Stéphane Chazelas,

3

Możesz także używać granic słów.

$ perl -pe 's/(?<=.)(\B|\b)(?=.)/\n/g' <<< "StackOver"
S
t
a
c
k
O
v
e
r

1

W bash:

Działa to z dowolnym tekstem i tylko z wewnętrznymi funkcjami bash (nie jest wywoływane zewnętrzne narzędzie), więc powinno być szybkie na bardzo krótkich ciągach.

str="Stéphane áàéèëêếe"

[[ $str =~ ${str//?/(.)} ]]
(set -- "${BASH_REMATCH[@]:1}"; IFS=$'\n'; echo "$*")

Wynik:

S
t
é
p
h
a
n
e

á
à
é
è
ë
ê
ế
e

Jeśli można zmienić IFS i parametry pozycyjne, można również uniknąć wywołania podpowłoki:

str="Stéphane áàéèëêếe"
[[ $str =~ ${str//?/(.)} ]]
set -- "${BASH_REMATCH[@]:1}"
IFS=$'\n'
echo "$*"

1
s=stackoverflow;

$ time echo $s | fold -w1                                                                                                                                          
s                                                                                                                                                                          
t                                                                                                                                                                          
a                                                                                                                                                                          
c                                                                                                                                                                          
k                                                                                                                                                                          
o                                                                                                                                                                          
v
e
r

real    0m0.014s
user    0m0.000s
sys     0m0.004s

aktualizacje tutaj to hacky | najszybszy | pureBashBased sposób!

$ time eval eval printf \'%s\\\\n\' \\\${s:\{0..$((${#s}-1))}:1}
s
t
a
c
k
o
v
e
r

real    0m0.001s
user    0m0.000s
sys     0m0.000s

po więcej niesamowitości

function foldh () 
{ 
    if (($#)); then
        local s="$@";
        eval eval printf \'%s\\\\n\' \\\"\\\${s:\{0..$((${#s}-1))}:1}\\\";
    else
        while read s; do
            eval eval printf \'%s\\\\n\' \\\"\\\${s:\{0..$((${#s}-1))}:1}\\\";
        done;
    fi
}
function foldv () 
{ 
    if (($#)); then
        local s="$@";
        eval eval echo \\\"\\\${s:\{0..$((${#s}-1))}:1}\\\";
    else
        while read s; do
            eval eval echo \\\"\\\${s:\{0..$((${#s}-1))}:1}\\\";
        done;
    fi
}

Czy to kiedykolwiek da inne wyniki fold -b1?
JigglyNaga,

ponieważ każdy bajt ma szerokość = 1, wynik będzie taki sam!
Jonah

1
Jak to nie jest duplikat wcześniejszej odpowiedzi ?
JigglyNaga,

ponieważ pokazuje ten sam cmd z różnymi argumentami, i dobrze jest wiedzieć.
Jonah

1
read -a var <<< $(echo "$yourWordhere" | grep -o "." | tr '\n' ' ')

to podzieli twoje słowo i zapisze je w tablicy var.


1
for x in $(echo "$yourWordhere" | grep -o '.')
do
    code to perform operation on individual character $x of your word
done
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.