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


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.