Script Bash: palavra dividida em cada letra

17

Como posso dividir as letras de uma palavra, com cada letra em uma linha separada?

Por exemplo, dado que "StackOver" eu gostaria de ver

S
t
a
c
k
O
v
e
r

Eu sou novo no bash, então não tenho idéia por onde começar.

Sijaan Hallak
fonte

Respostas:

29

Eu usaria grep:

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

ou sed:

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

E se o espaço vazio no final for um problema:

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

Tudo isso assumindo o GNU / Linux.

jimmij
fonte
grep -o. <<< ¿¿¿.. -o procura pelo PADRÃO fornecido certo? e o que faz aqui em seu comando?
Sijaan Hallak
1
@jimmij Não consigo encontrar nenhuma ajuda sobre o que <<< realmente faz! qualquer ajuda?
Sijaan Hallak
3
@SijaanHallak Isso é chamado Here string, grosso modo equivalente a echo foo | ...apenas menos digitação. Veja tldp.org/LDP/abs/html/x17837.html
jimmij
1
@SijaanHallak muda .para \B(não corresponde ao limite da palavra).
jimmij
1
@SijaanHallak - você pode deixar cair o segundo sedcomo:sed -et -e's/./\n&/g;//D'
mikeserv
19

Convém quebrar os clusters de grafema em vez de caracteres, se a intenção for imprimir texto verticalmente. Por exemplo, com um esotaque agudo:

  • Com clusters de grafema ( ecom seu sotaque agudo seria um cluster de grafema):

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

    (ou grep -Po '\X'com o GNU grep criado com suporte para PCRE)

  • Com caracteres (aqui com GNU grep):

    $ printf '%s\n' $'Ste\u301phane' | grep -o .
    S
    t
    e
    
    p
    h
    a
    n
    e
    
  • folddestina-se a quebrar caracteres, mas o GNU foldnão suporta caracteres de bytes múltiplos; portanto, quebra em bytes:

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

No StackOver, que consiste apenas em caracteres ASCII (portanto, um byte por caractere, um caractere por cluster de grafema), os três forneceriam o mesmo resultado.

Stéphane Chazelas
fonte
Estou surpreso grep -Poque não faça o que se esperaria (como grep -Pfaz).
jimmij
@jimmij, o que você quer dizer? grep -Po .encontra caracteres (e um sotaque agudo combinado após um caractere de nova linha é inválido) e grep -Po '\X'encontra grupos de graphem para mim. Você pode precisar de uma versão recente do grep e / ou PCRE para que ele funcione corretamente (ou tentar grep -Po '(*UTF8)\X')
Stéphane Chazelas
6

Se você tiver perl6 em sua caixa:

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

trabalhar independentemente do seu local.

cuonglm
fonte
6

Com muitas awkversões

awk -F '' -v OFS='\n' '{$1=$1};1' <<<'StackOver'
iruvar
fonte
Ótimo! Mas na minha versão do nAWK ("One True AWK") isso não funciona. No entanto, este faz o truque: awk -v FS='' -v OFS='\n' '{$1=$1};1' (perguntando se isso é mais portátil já que -F ''pode produzir o ERE: //)
eruve
4

O abaixo será genérico:

$ awk -F '' \
   'BEGIN { RS = ""; OFS = "\n"} {for (i=1;i<=NF;i++) $i = $i; print }' <file_name>
user150073
fonte
4
echo StackOver | sed -e 's/./&\n/g'
S
t
a
c
k
O
v
e
r
Henderson
fonte
Isso não vai ajudar como ele imprime uma nova linha no final
Sijaan Hallak
4

Como você pediu especificamente uma resposta no bash, eis uma maneira de fazê-lo no bash puro:

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

Observe que isso capturará a nova linha no final do " documento aqui ". Se você quiser evitar isso, mas ainda repetir os caracteres com um loop bash, use printfpara evitar a nova linha.

printf StackOver | while read -rn1; do echo "$REPLY" ; done
wyrm
fonte
4

Também Python 2 pode ser usado na linha de comando:

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

ou:

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

ou (como comentado por 1_CR) com Python 3 :

python3 -c "print(*'StackOver',sep='\n')"
agold
fonte
4

Você pode usar o fold (1)comando É mais eficiente que grepe 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
$

Uma diferença significativa é que a dobra reproduzirá linhas vazias na saída:

$ 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
fonte
3

Você pode manipular caracteres multibyte como:

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

O que pode ser bastante útil quando você estiver trabalhando com entrada ao vivo, porque não há buffer e um caractere é impresso assim que ele é inteiro .

mikeserv
fonte
NP, devemos adicionar uma observação sobre a localidade?
precisa saber é
Não funciona para combinar caracteres como a resposta de Stéphane Chazelas, mas com normalização adequada isso não deve importar.
kay está decepcionado em SE
@Kay - funciona para combinar caracteres, se você quiser - é para isso que sedservem os scripts. Não é provável que eu escreva uma sobre isso agora - estou com muito sono. é realmente útil, porém, ao ler um terminal.
mikeserv
@cuonglm - se quiser. ele deve funcionar apenas para o código do idioma, com uma sã libc, no entanto.
mikeserv
Observe que ddisso quebrará caracteres multibyte, portanto a saída não será mais texto, portanto o comportamento do sed não será especificado conforme POSIX.
Stéphane Chazelas
3

Você também pode usar limites de palavras.

$ perl -pe 's/(?<=.)(\B|\b)(?=.)/\n/g' <<< "StackOver"
S
t
a
c
k
O
v
e
r
Avinash Raj
fonte
1

Na festança:

Isso funciona com qualquer texto e apenas com internos do bash (nenhum utilitário externo chamado), portanto, deve ser rápido em seqüências muito curtas.

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

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

Resultado:

S
t
é
p
h
a
n
e

á
à
é
è
ë
ê
ế
e

Se estiver correto alterar o IFS e alterar os parâmetros posicionais, você também pode evitar a chamada sub shell:

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

atualizações aqui é a maneira hacky | mais rápida | 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

para mais grandiosidade

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
}
Jonah
fonte
Isso sempre dará resultados diferentes fold -b1?
JGGLYNAGA
como cada byte tem uma largura = 1, o resultado será o mesmo!
Jonah
1
Então, como isso não é uma duplicata da resposta anterior ?
JGGLYNAGA
porque mostra o mesmo cmd com argumentos diferentes, e isso é bom saber.
Jonah
1
read -a var <<< $(echo "$yourWordhere" | grep -o "." | tr '\n' ' ')

isso dividirá sua palavra e a armazenará em matriz var.

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