Скрипт Bash: разбить слово на каждую букву

17

Как я могу разделить буквы слова с каждой буквой в отдельной строке?

Например, учитывая, что "StackOver" я хотел бы видеть

S
t
a
c
k
O
v
e
r

Я новичок в bash, поэтому понятия не имею, с чего начать.

Сияан Халлак
источник

Ответы:

29

Я бы использовал grep:

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

или sed:

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

И если пустое место в конце является проблемой:

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

Все это при условии GNU / Linux.

jimmij
источник
grep -o. <<< ¿¿¿.. -o ищет предоставленный ШАБЛОН, верно? и что он здесь делает в вашей команде?
Sijaan Hallak
1
@jimmij Я не могу найти никакой помощи в том, что <<< действительно делает! любая помощь?
Сияан Халлак
3
@SijaanHallak Это так называемый Here stringгроссо-модо, эквивалентный echo foo | ...просто меньшему количеству печатания. См. Tldp.org/LDP/abs/html/x17837.html
jimmij
1
@SijaanHallak изменяется .на \B(не соответствует границе слова).
Джимми
1
@SijaanHallak - вы можете оставить второй, sedкак:sed -et -e's/./\n&/g;//D'
mikeserv
19

Вы можете разбивать кластеры графем вместо символов, если вы хотите печатать текст вертикально. Например, eс острым акцентом:

  • С кластерами графем ( eс его острым акцентом будет один кластер графем):

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

    (или grep -Po '\X'с GNU grep, созданным с поддержкой PCRE)

  • С символами (здесь с GNU grep):

    $ printf '%s\n' $'Ste\u301phane' | grep -o .
    S
    t
    e
    
    p
    h
    a
    n
    e
    
  • foldпредназначен для разбиения на символы, но GNU foldне поддерживает многобайтовые символы, поэтому вместо этого он разбивает на байты:

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

В StackOver, который состоит только из символов ASCII (то есть один байт на символ, один символ на кластер графем), все три будут давать одинаковый результат.

Стефан Шазелас
источник
Я удивлен grep -Po, не делает то, что можно ожидать (как grep -Pделает).
Джимми
@jimmij, что ты имеешь в виду? grep -Po .находит символы (и комбинированный острый акцент после символа новой строки недопустим) и grep -Po '\X'находит кластеры графема для меня. Вам может понадобиться последняя версия grep и / или PCRE, чтобы она работала должным образом (или попробуйте grep -Po '(*UTF8)\X')
Стефан Шазелас
2
@SijaanHallak Это может быть полезно: joelonsoftware.com/articles/Unicode.html , eev.ee/blog/2015/09/12/dark-corners-of-unicode
jpmc26
6

Если у вас есть perl6 в вашей коробке:

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

работать независимо от вашей локали.

cuonglm
источник
6

Со многими awkверсиями

awk -F '' -v OFS='\n' '{$1=$1};1' <<<'StackOver'
Iruvar
источник
Большой! Но на моей версии nAWK («One True AWK») это не работает. Однако это делает трюк: awk -v FS='' -v OFS='\n' '{$1=$1};1' (интересно , если это более компактен , так как -F ''может дать в ERE: //)
eruve
4

Ниже будет общим:

$ awk -F '' \
   'BEGIN { RS = ""; OFS = "\n"} {for (i=1;i<=NF;i++) $i = $i; print }' <file_name>
user150073
источник
4
echo StackOver | sed -e 's/./&\n/g'
S
t
a
c
k
O
v
e
r
гендерсон
источник
Это не поможет, так как печатает новую строку в конце
Sijaan Hallak
4

Поскольку вы специально запросили ответ в bash, вот способ сделать это в чистом bash:

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

Обратите внимание, что это поймает новую строку в конце « здесь документа ». Если вы хотите избежать этого, но по-прежнему перебираете символы с помощью цикла bash, используйте, printfчтобы избежать перехода на новую строку.

printf StackOver | while read -rn1; do echo "$REPLY" ; done
вирм
источник
4

Также Python 2 можно использовать из командной строки:

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

или:

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

или (как прокомментировано 1_CR) с Python 3 :

python3 -c "print(*'StackOver',sep='\n')"
Agold
источник
4

Вы можете использовать fold (1)команду. Это более эффективно, чем grepи 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
$

Одно существенное различие заключается в том, что в выводе fold будут воспроизводиться пустые строки:

$ 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
источник
3

Вы можете обрабатывать многобайтовые символы, такие как:

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

Что может быть очень удобно, когда вы работаете с живым вводом, потому что там нет буферизации и символ печатается, как только он становится целым .

mikeserv
источник
NP, мы должны добавить примечание о локали?
Cuonglm
Не работает для объединения символов, как ответ Стефана Шазеля, но при правильной нормализации это не должно иметь значения.
Кей разочарован в SE
@ Kay - это работает для объединения символов, если вы хотите - это то sed, для чего нужны сценарии. я вряд ли напишу об этом прямо сейчас - я довольно сонный. это действительно полезно, хотя, при чтении терминала.
mikeserv
@cuonglm - если хочешь. это должно только работать для локали, учитывая нормальный libc, все же.
mikeserv
Обратите внимание, что это ddприведет к разрыву многобайтовых символов, поэтому выходные данные больше не будут текстовыми, поэтому поведение sed будет неопределенным в соответствии с POSIX.
Стефан Шазелас
3

Вы можете использовать границы слов также ..

$ perl -pe 's/(?<=.)(\B|\b)(?=.)/\n/g' <<< "StackOver"
S
t
a
c
k
O
v
e
r
Авинаш Радж
источник
1

В Баш:

Это работает с любым текстом и только с внутренними компонентами bash (внешняя утилита не вызывается), поэтому должна быть быстрой на очень коротких строках.

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

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

Выход:

S
t
é
p
h
a
n
e

á
à
é
è
ë
ê
ế
e

Если можно изменить IFS и позиционные параметры, вы также можете избежать вызова sub-shell:

str="Stéphane áàéèëêếe"
[[ $str =~ ${str//?/(.)} ]]
set -- "${BASH_REMATCH[@]:1}"
IFS=$'\n'
echo "$*"
sorontar
источник
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

обновления здесь - это хакерский | быстрый | pureBashBased способ!

$ 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

для большей удивительности

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
}
Ион
источник
Даст ли это когда-нибудь разные результаты fold -b1?
JigglyNaga
поскольку каждый байт имеет ширину = 1, результат будет одинаковым!
Иона
1
Так как же это не дубликат предыдущего ответа ?
JigglyNaga
потому что он показывает тот же cmd с другим аргументом, и это приятно знать.
Иона
1
read -a var <<< $(echo "$yourWordhere" | grep -o "." | tr '\n' ' ')

это разделит ваше слово и сохранит его в массиве var.

Чинмай Катиль
источник
1
for x in $(echo "$yourWordhere" | grep -o '.')
do
    code to perform operation on individual character $x of your word
done
Чинмай Катиль
источник