He estado tratando de usar bash para leer un archivo carácter por carácter.
Después de mucho ensayo y error, descubrí que esto funciona:
exec 4<file.txt
declare -i n
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, lo intenté,
exec 4<file.txt && while read -r -n1 ch <&4; do; echo -n "$ch"; done
pero omitiría todos los espacios en blanco en el archivo .
¿Podría explicar por qué? ¿Hay alguna manera de hacer que la segunda estrategia (es decir, leer char por char con bash read) funcione?
IFS
en nada para que los espacios en blanco sobrevivan a la división de palabras.Respuestas:
Debe eliminar los caracteres de espacio en blanco del
$IFS
parámetro pararead
dejar de omitir los caracteres iniciales y finales (con-n1
el carácter de espacio en blanco si alguno sería tanto inicial como final, por lo que se omite):Pero incluso entonces, bash
read
omitirá los caracteres de nueva línea, con los que puede trabajar:Aunque podría usar
IFS= read -d '' -rn1
en su lugar o incluso mejorIFS= read -N1
(agregado en 4.1, copiado deksh93
(agregadoo
)), que es el comando para leer un carácter.Tenga en cuenta que bash
read
no puede hacer frente a los caracteres NUL. Y ksh93 tiene los mismos problemas que bash.Con zsh:
(zsh puede hacer frente a caracteres NUL).
Tenga en cuenta que esos
read -k/n/N
leen una cantidad de caracteres , no bytes . Entonces, para los caracteres multibyte, pueden tener que leer varios bytes hasta que se lea un carácter completo. Si la entrada 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 contar como varios caracteres . Por ejemplo, en un entorno local UTF-8:Eso
\375
introduciría un carácter UTF-8 de 6 bytes. Sin embargo, el sexto (A
) anterior no es válido para un carácter UTF-8. Todavía terminas con\375\200\200\200\200A
in$a
, quebash
cuenta como 6 caracteres, aunque los primeros 5 no son realmente caracteres, solo 5 bytes no forman parte de ningún carácter.fuente
read -rN1
resuelve el problema de la nueva línea y, por lo tanto, elimina la necesidad de proporcionar una nueva línea por defecto al imprimir$a
.read -n1
(char por char) toma 4 min 51 segundos y calienta la computadora portátil a 90 grados. El usoread -r
(línea por línea) toma 1.3 segundos y la computadora portátil se mantiene a 54 grados con dos ventiladores silenciosos.Este es un ejemplo simple usando
cut
, unfor
bucle &wc
:BESO no es así?
fuente
bash
solución purafile="$(</etc/passwd)"; bytes="${#file}"; for ((i=0;i<bytes;i++)); do echo "${file:i:1}"; done
?bash
"Es demasiado grande y demasiado lento". de acuerdo con la sección BUGS de su página de manual. Pero aun así, es más rápido cortar una cadena en la memoria que leer un archivo una y otra vez para cada carácter. Al menos en mi máquina: pastebin.com/zH5trQQs