
Tengo un archivo llamado que test
contiene
test
test
y estoy ejecutando este comando
while read line
do
echo "$line"
done </tmp/test
Esto debería generar "prueba" dos veces, pero solo lo genera una vez. Usarlo while IFS= read -r line
no cambia nada. Algo que lo soluciona es generar una tercera línea vacía, pero debería funcionar de todos modos.
Respuesta1
Intentar:
while IFS='' read -r line || [ "$line" ]
Sí, está leyendo desde la entrada estándar (stdin), la <test
redirección va a la entrada estándar.
Lo que podría estar pasando es que read está diseñado para funcionar con archivos de texto.Los archivos de texto correctos deben terminar en una nueva línea.
Hacer unEl final del archivo en una nueva línea resulta ser muy rápido.:
[ -n "$(tail -c1 file)" ] && printf '\n' >>file
Pero si no puedes editar el archivo para agregar una nueva línea (si falta), la solución es probar (adicionalmente) si se leyó algo:
while read line || [ "$line" ]
Eso hará que el bucle se ejecute si la lectura falló (llegó al final del archivo sin leer una nueva línea (de forma predeterminada)) pero se leyó algo (en "$line"
).
Eso funcionará correctamente para archivos que terminen en una nueva línea o no.
No, a IFS='' read
no afectará (directamente) la lectura de la última línea; de hecho, IFS=''
(en comparación con un IFS predeterminado[a]de espacio, tabulación, nueva línea) sólo afectarán la división envariosvariables (además de afectar la eliminación de espacios en blanco iniciales y/o finales (si IFS contiene solo espacios en blanco)[b])). Como solo hay una variable ( "$line"
), no es necesario realizar ninguna división.
La -d
opción de leer (en bash desde 2.04) tampoco ayudará. No hay ningún carácter específico de "fin de archivo" que coincida (el último byte puede ser cualquiera).
Las únicas opciones que quedan son:
- reparar el archivo para convertirlo en un archivo de texto correcto
- Pruebe si se leyó algo en la variable
line
.
El script correcto entonces será:
#!/bin/bash
while IFS='' read -r line || [ "$line" ]
do
printf '%s\n' "$line"
done <test
IFS='' se utiliza para evitar problemas con valores poco comunes de IFS.
[a][b]Por supuesto, si IFS puede tener algún valor, es posible que se eliminen muchos otros caracteres. Pruebe (para valores que terminan en 'x')
printf "test\ntesx\ntest" | while IFS="x" read -r line || [ "$line" ]; do echo "$line"; done
No, zsh hace algo diferente.