Me sorprende que nadie haya mencionado la bashsolución obvia utilizando solo whiley read.
while read -n1 character; do
echo "$character"
done < <(echo -n "$words")
Tenga en cuenta el uso de echo -npara evitar el salto de línea extraño al final. printfes otra buena opción y puede ser más adecuada para sus necesidades particulares. Si desea ignorar los espacios en blanco, reemplace "$words"con "${words// /}".
Otra opción es fold. Sin embargo, tenga en cuenta que nunca debe introducirse en un bucle for. Más bien, use un ciclo while de la siguiente manera:
while read char; do
echo "$char"
done < <(fold -w1 <<<"$words")
El beneficio principal de usar el foldcomando externo (del paquete coreutils ) sería la brevedad. Puede alimentar su salida a otro comando como xargs(parte del paquete findutils ) de la siguiente manera:
fold -w1 <<<"$words" | xargs -I% -- echo %
Querrá reemplazar el echocomando utilizado en el ejemplo anterior con el comando que le gustaría ejecutar contra cada personaje. Tenga en cuenta que xargsdescartará los espacios en blanco de forma predeterminada. Puede utilizar -d '\n'para deshabilitar ese comportamiento.
Internacionalización
Acabo de probar foldcon algunos de los caracteres asiáticos y me di cuenta de que no es compatible con Unicode. Entonces, aunque está bien para las necesidades de ASCII, no funcionará para todos. En ese caso existen algunas alternativas.
Probablemente lo reemplazaría fold -w1con una matriz awk:
awk 'BEGIN{FS=""} {for (i=1;i<=NF;i++) print $i}'
O el grepcomando mencionado en otra respuesta:
grep -o .
Actuación
FYI, comparé las 3 opciones mencionadas anteriormente. Los dos primeros fueron rápidos, casi atados, con el bucle de plegado un poco más rápido que el bucle while. Como xargsera de esperar, fue el más lento ... 75 veces más lento.
Aquí está el código de prueba (abreviado):
words=$(python -c 'from string import ascii_letters as l; print(l * 100)')
testrunner(){
for test in test_while_loop test_fold_loop test_fold_xargs test_awk_loop test_grep_loop; do
echo "$test"
(time for (( i=1; i<$((${1:-100} + 1)); i++ )); do "$test"; done >/dev/null) 2>&1 | sed '/^$/d'
echo
done
}
testrunner 100
Aquí están los resultados:
test_while_loop
real 0m5.821s
user 0m5.322s
sys 0m0.526s
test_fold_loop
real 0m6.051s
user 0m5.260s
sys 0m0.822s
test_fold_xargs
real 7m13.444s
user 0m24.531s
sys 6m44.704s
test_awk_loop
real 0m6.507s
user 0m5.858s
sys 0m0.788s
test_grep_loop
real 0m6.179s
user 0m5.409s
sys 0m0.921s