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.