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.