Баш сценарій: розділити слово на кожну букву


17

Як я можу розділити літери слова з кожною буквою в окремому рядку?

Наприклад, враховуючи, що "StackOver" я хотів би бачити

S
t
a
c
k
O
v
e
r

Я новачок у баші, тому у мене немає поняття, з чого почати.

Відповіді:


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.


греп -о. <<< ¿¿¿.. - про пошук ПАТЕРНУ, наданий правильно? і що він робить тут у вашій команді?
Сіджан Галлак

1
@jimmij Я не можу знайти будь-якої допомоги щодо того, що <<< насправді робить! будь-яка допомога?
Сіджан Галлак

3
@SijaanHallak Це, так званий Here string, еквівалент бруто-моду echo foo | ...лише меншого набору тексту. Дивіться tldp.org/LDP/abs/html/x17837.html
jimmij

1
@SijaanHallak змінити .на \B(не відповідає межі слова).
jimmij

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

@jimmij, що ти маєш на увазі? grep -Po .знаходить символів (а поєднання гострого акценту за символом нового рядка є недійсним) та grep -Po '\X'знаходить кластери графеми для мене. Вам може знадобитися остання версія grep та / або PCRE, щоб вона працювала належним чином (або спробуйте grep -Po '(*UTF8)\X')
Stéphane Chazelas

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

працювати незалежно від вашої мови.


6

З багатьма awkверсіями

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

Чудово! Але в моїй версії 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>


4

Оскільки ви спеціально попросили відповіді в 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')"

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
$

Важливою відмінністю є те, що складка відтворює порожні рядки у висновку:

$ 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

Ви можете обробляти багатобайтові символи, наприклад:

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

Що може бути дуже зручно, коли ви працюєте з введенням в реальному часі, оскільки там немає буферизації, і символ друкується, як тільки він стає цілим .


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:

Це працює з будь-яким текстом і лише з внутрішніми файлами bash (жодна зовнішня утиліта не називається), тож має бути швидкою на дуже коротких рядках.

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

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

Вихід:

S
t
é
p
h
a
n
e

á
à
é
è
ë
ê
ế
e

Якщо нормально змінити IFS та змінити позиційні параметри, ви також можете уникнути виклику підрозділу:

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

оновлення тут - хекітний | найшвидший | pureBashBase спосіб!

$ 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.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.