Lectura de personaje por personaje con lectura de bash

He intentado usar bash para leer un file carácter por caracter.

Después de mucho ensayo y error, descubrí que esto funciona:

exec 4<file.txt declare -in while read -r ch <&4; n=0 while [ ! $n -eq ${#ch} ] do echo -n "${ch:$n:1}" (( n++ )) done echo "" done 

Es decir, puedo leerlo línea por línea y luego recorrer cada línea char por char.

Antes de hacer esto, había intentado: exec 4<file.txt && while read -r -n1 ch <&4; do; echo -n "$ch"; done exec 4<file.txt && while read -r -n1 ch <&4; do; echo -n "$ch"; done exec 4<file.txt && while read -r -n1 ch <&4; do; echo -n "$ch"; done pero saltaría todos los espacios en blanco en el file .

¿Podría explicar por qué? ¿Hay alguna manera de hacer funcionar la segunda estrategia (es decir, leer char por char con bash's read)?

Necesita eliminar los espacios en blanco del parámetro $IFS para read para detener la omisión de los principales y finales (con -n1 , el carácter de espacio en blanco, si alguno sería al -n1 y al final, omitido):

 while IFS= read -rn1 a; do printf %s "$a"; done 

Pero incluso entonces, la read de bash omitirá los caracteres de nueva línea, con lo cual puedes trabajar:

 while IFS= read -rn1 a; do printf %s "${a:-$'\n'}"; done 

Aunque podría usar IFS= read -d '' -rn1 en IFS= read -d '' -rn1 lugar o incluso mejor IFS= read -N1 (agregado en 4.1, copydo de ksh93 (agregado en o )) que es el command para leer un carácter.

Tenga en count que la read de bash no puede hacer frente a los caracteres NUL. Y ksh93 tiene los mismos problemas que bash.

Con zsh:

 while read -ku0 a; do print -rn -- "$a"; done 

(zsh puede hacer frente a los caracteres NUL).

Tenga en count que aquellos que read -k/n/N leen una cantidad de caracteres , no de bytes . Por lo tanto, para caracteres multibyte, es posible que tengan que leer varios bytes hasta que se lea un carácter completo. Si la input contiene caracteres no válidos, puede terminar con una variable que contiene una secuencia de bytes que no forma caracteres válidos y que el shell puede terminar contando como varios caracteres . Por ejemplo, en una configuration regional UTF-8:

 $ printf '\375\200\200\200\200ABC' | bash -c ' IFS= read -rN1 a; echo "${#a}"' 6 

Eso \375 introduciría un carácter UTF-8 de 6 bytes. Sin embargo, el sexto ( A ) anterior no es válido para un personaje UTF-8. Todavía terminas con \375\200\200\200\200A en $a , que bash count como 6 caracteres, aunque los primeros 5 no son realmente personajes, solo 5 bytes no forman parte de ningún personaje.

Este es un ejemplo simple usando cut , a for loop & wc :

 bytes=$(wc -c < /etc/passwd) file=$(</etc/passwd) for ((i=0; i<bytes; i++)); do echo $file | cut -c $i done 

KISS, ¿no es así?