¿Cómo usar 'while read' (Bash) para leer la última línea de un archivo si no hay una nueva línea al final del archivo?
Digamos que tengo el siguiente script Bash:
while read SCRIPT_SOURCE_LINE; do
echo "$SCRIPT_SOURCE_LINE"
done
Me di cuenta de que para los archivos sin una nueva línea al final, esto saltará efectivamente la última línea.
He buscado una solución y he encontrado esto :
Cuando read llega al final del archivo de fin de línea, se lee en el datos y asignarlos a las variables, pero sale con un estado distinto de cero. Si su bucle está construido " mientras leer, hacer cosas ;hecho
So en lugar de probar la salida de lectura estado directamente, probar una bandera, y tener el comando read establece ese indicador desde dentro del cuerpo del bucle. De esa manera independientemente del estado de salida de las lecturas, todo el cuerpo del bucle se ejecuta, porque leer era solo uno de la lista de comandos en el bucle como cualquier otro, no factor decisivo de si el bucle se vete corriendo.
DONE=false until $DONE ;do read || DONE=true # process $REPLY here done < /path/to/file.in
¿Cómo puedo reescribir esta solución para que se comporte exactamente igual que el bucle while
que estaba teniendo antes, es decir, sin hardcoding la ubicación del archivo de entrada?
7 answers
En tu primer ejemplo, asumo que estás leyendo desde stdin. Para hacer lo mismo con el segundo bloque de código, solo tienes que eliminar la redirección y echo REPLY REPLY:
DONE=false
until $DONE ;do
read || DONE=true
echo $REPLY
done
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-12 13:41:40
Utilizo la siguiente construcción:
while IFS= read -r LINE || [[ -n "$LINE" ]]; do
echo "$LINE"
done
Funciona con casi cualquier cosa excepto caracteres nulos en la entrada:
- Archivos que comienzan o terminan con líneas en blanco
- Líneas que comienzan o terminan con espacios en blanco
- Archivos que no tienen una nueva línea de terminación
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-10 11:46:22
Usando grep
con bucle while:
while IFS= read -r line; do
echo "$line"
done < <(grep "" file)
Usando grep .
en lugar de grep ""
saltará las líneas vacías.
Nota:
Usar
IFS=
mantiene intacta cualquier sangría de línea.El archivo sin una nueva línea al final no es un archivo de texto estándar de unix.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-14 05:04:16
En Lugar de leer, pruebe a utilizar GNU Coreutils como tee, cat, etc.
De stdin
readvalue=$(tee)
echo $readvalue
Desde el archivo
readvalue=$(cat filename)
echo $readvalue
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-03 14:29:35
El problema básico aquí es que read
devolverá errorlevel 1 cuando encuentre EOF, incluso si todavía alimentará correctamente la variable.
Así que puede usar errorlevel de read
de inmediato en su bucle, otherwize, los últimos datos no se analizarán. Pero usted podría hacer esto:
eof=
while [ -z "$eof" ]; do
read SCRIPT_SOURCE_LINE || eof=true ## detect eof, but have a last round
echo "$SCRIPT_SOURCE_LINE"
done
Si desea una forma muy sólida de analizar sus líneas, debe usar:
IFS='' read -r LINE
Recuerda que:
- El carácter NUL será ignorado
- si sigues usando
echo
para imitar el comportamiento decat
tendrá que forzar unecho -n
sobre EOF detectado (puede usar la condición[ "$eof" == true ]
)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-03 09:18:48
La respuesta de@Netcoder es buena, esta optimización elimina las líneas en blanco espurias, también permite que la última línea no tenga una nueva línea, si así es como era la original.
DONE=false
NL=
until $DONE ;do
if ! read ; then DONE=true ; NL='-n ';fi
echo $NL$REPLY
done
Usé una variante de esto para crear 2 funciones para permitir la canalización de texto que incluye un '[' para mantener a grep feliz. (puede agregar otras traducciones)
function grepfix(){
local x="$@";
if [[ "$x" == '-' ]]; then
local DONE=false
local xx=
until $DONE ;do
if ! IFS= read ; then DONE=true ; xx="-n "; fi
echo ${xx}${REPLY//\[/\\\[}
done
else
echo "${x//\[/\\\[}"
fi
}
function grepunfix(){
local x="$@";
if [[ "$x" == '-' ]]; then
local DONE=false
local xx=
until $DONE ;do
if ! IFS= read ; then DONE=true ; xx="-n "; fi
echo ${xx}${REPLY//\\\[/\[}
done
else
echo "${x//\\\[/\[}"
fi
}
(passing-as enables 1 habilita pipe de lo contrario solo traduce argumentos)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-07 09:44:34
Este es el patrón que he estado usando:
while read -r; do
echo "${REPLY}"
done
[[ ${REPLY} ]] && echo "${REPLY}"
Que funciona porque incluso tho' el bucle while
termina como la "prueba" de la read
sale con un código distinto de cero, read
todavía llena la variable incorporada $REPLY
(o cualquier variable que elija asignar con read
).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-06 17:24:58