Script Bash: palabra dividida en cada letra


17

¿Cómo puedo dividir las letras de una palabra, con cada letra en una línea separada?

Por ejemplo, dado que "StackOver" me gustaría ver

S
t
a
c
k
O
v
e
r

Soy nuevo en bash, así que no tengo idea de por dónde empezar.

Respuestas:


29

Yo usaría grep:

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

o sed:

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

Y si el espacio vacío al final es un problema:

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

Todo eso suponiendo GNU / Linux.


grep -o. <<< ¿¿¿.. -o busca el PATRÓN proporcionado, ¿verdad? ¿Y qué hace aquí a tus órdenes?
Sijaan Hallak

1
@jimmij ¡No puedo encontrar ayuda sobre lo que <<< realmente hace! ¿alguna ayuda?
Sijaan Hallak el

3
@SijaanHallak Esto se llama Here stringgrosso modo equivalente de echo foo | ...escribir menos. Ver tldp.org/LDP/abs/html/x17837.html
jimmij el

1
@SijaanHallak cambia .a \B(no coincide con el límite de la palabra).
jimmij

1
@SijaanHallak - puedes soltar el segundo sedcomo:sed -et -e's/./\n&/g;//D'
mikeserv

19

Es posible que desee romper en grupos de grafemas en lugar de caracteres si la intención es imprimir texto verticalmente. Por ejemplo, con un eacento agudo:

  • Con los grupos de grafemas ( econ su acento agudo sería un grupo de grafemas):

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

    (o grep -Po '\X'con GNU grep construido con soporte PCRE)

  • Con caracteres (aquí con GNU grep):

    $ printf '%s\n' $'Ste\u301phane' | grep -o .
    S
    t
    e
    
    p
    h
    a
    n
    e
    
  • foldestá destinado a dividir caracteres, pero GNU foldno admite caracteres de varios bytes, por lo que se divide en bytes:

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

En StackOver, que solo consta de caracteres ASCII (un byte por carácter, un carácter por grupo de grafemas), los tres darían el mismo resultado.


Me sorprende grep -Poque no haga lo que uno esperaría (como grep -Phace).
jimmij

@jimmij, ¿qué quieres decir? grep -Po .encuentra caracteres (y un acento agudo combinado después de un carácter de nueva línea no es válido), y grep -Po '\X'encuentra grupos de gráficos para mí. Es posible que necesite una versión reciente de grep y / o PCRE para que funcione correctamente (o intente grep -Po '(*UTF8)\X')
Stéphane Chazelas


6

Si tienes perl6 en tu caja:

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

trabajar independientemente de su localidad.


6

Con muchas awkversiones

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

¡Excelente! Pero en mi versión de nAWK ("One True AWK") eso no funciona. Sin embargo, esto hace el truco: awk -v FS='' -v OFS='\n' '{$1=$1};1' (preguntando si eso es más portátil, ya -F ''que podría dar lugar al ERE: //)
eruve

4

Lo siguiente será genérico:

$ awk -F '' \
   'BEGIN { RS = ""; OFS = "\n"} {for (i=1;i<=NF;i++) $i = $i; print }' <file_name>


4

Como solicitó específicamente una respuesta en bash, aquí hay una manera de hacerlo en bash puro:

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

Tenga en cuenta que esto capturará la nueva línea al final del " documento aquí ". Si desea evitar eso, pero aún itera sobre los caracteres con un bucle bash, use printfpara evitar la nueva línea.

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

4

También se puede usar Python 2 desde la línea de comandos:

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

o:

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

o (como lo comentó 1_CR) con Python 3 :

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

4

Puedes usar el fold (1)comando. Es más eficiente que grepy 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
$

Una diferencia significativa es que el pliegue reproducirá líneas vacías en la salida:

$ 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

Puede manejar caracteres multibyte como:

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

Lo que puede ser bastante útil cuando trabajas con entrada en vivo porque no hay almacenamiento en búfer allí y se imprime un personaje tan pronto como está completo .


NP, ¿deberíamos agregar una nota sobre la configuración regional?
Cuonglm

No funciona para combinar caracteres como la respuesta de Stéphane Chazelas, pero con la normalización adecuada esto no debería importar.
Kay está decepcionado en SE

@Kay: funciona para combinar caracteres si lo desea , para eso sedestán los scripts. No es probable que escriba uno ahora mismo, tengo mucho sueño. Sin embargo, es realmente útil cuando se lee un terminal.
mikeserv

@cuonglm, si quieres. Sin embargo, debería funcionar para la configuración regional, dada una libc sana.
mikeserv

Tenga en cuenta que ddromperá los caracteres multibyte, por lo que la salida ya no será texto, por lo que el comportamiento de sed no se especificará según POSIX.
Stéphane Chazelas

3

También puede usar límites de palabras.

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

1

En bash:

Esto funciona con cualquier texto y solo con elementos internos de bash (no se llama una utilidad externa), por lo tanto, debe ser rápido en cadenas muy cortas.

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

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

Salida:

S
t
é
p
h
a
n
e

á
à
é
è
ë
ê
ế
e

Si está bien cambiar IFS y cambiar los parámetros posicionales, también puede evitar la llamada de sub-shell:

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

actualizaciones aquí es la manera hacky | rápido | 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 más genialidad

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
}

¿Esto alguna vez dará resultados diferentes fold -b1?
JigglyNaga

Como cada byte tiene un ancho = 1, ¡el resultado será el mismo!
Jonás

1
Entonces, ¿cómo no es esto un duplicado de la respuesta anterior ?
JigglyNaga

porque muestra el mismo cmd con diferente argyment, y eso es bueno saberlo.
Jonás

1
read -a var <<< $(echo "$yourWordhere" | grep -o "." | tr '\n' ' ')

esto dividirá su palabra y la almacenará en una matriz var.


1
for x in $(echo "$yourWordhere" | grep -o '.')
do
    code to perform operation on individual character $x of your word
done
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.