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ąć.

Sijaan Hallak
źródło

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.

jimmij
źródło
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.

Stéphane Chazelas
źródło
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.

Cuonglm
źródło
6

Z wieloma awkwersjami

awk -F '' -v OFS='\n' '{$1=$1};1' <<<'StackOver'
iruvar
źródło
Ś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>
użytkownik150073
źródło
4
echo StackOver | sed -e 's/./&\n/g'
S
t
a
c
k
O
v
e
r
henderson
źródło
To nie pomoże, ponieważ drukuje nową linię na końcu
Sijaan Hallak
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
wyrm
źródło
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')"
ponownie
źródło
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
$ 
joeytwiddle
źródło
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 .

mikeserv
źródło
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
Avinash Raj
źródło
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 "$*"
sorontar
źródło
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
}
Jonasz
źródło
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.

Chinmay Katil
źródło
1
for x in $(echo "$yourWordhere" | grep -o '.')
do
    code to perform operation on individual character $x of your word
done
Chinmay Katil
źródło