Mientras que bucle para probar si existe un archivo en bash
94
Estoy trabajando en un script de shell que hace ciertos cambios en un archivo txt solo si existe, sin embargo, este ciclo de prueba no funciona, me pregunto por qué. ¡Gracias!
No puedo decir que me sorprenda; ese bucle no intenta cambiar nada.
Ignacio Vazquez-Abrams
El punto y coma es redundante. ¿De qué manera no funciona ese ciclo de prueba? Se dormirá iterativamente durante 2 segundos hasta que exista el archivo /tmp/list.txt.
Jonathan Leffler
5
Funciona para mí: el bucle termina cuando el archivo se crea fuera del script.
1
de hecho, este bucle solo sirve para esperar hasta que el archivo esté allí, el resto de mi script hace los cambios ...: p
Zenet
1
Entonces el ciclo while funciona, solo soy yo ... lo siento.
Zenet
Respuestas:
145
Cuando dice "no funciona", ¿cómo sabe que no funciona?
Puede intentar averiguar si el archivo realmente existe agregando:
while[!-f /tmp/list.txt ]do
sleep 2# or less like 0.2done
ls -l /tmp/list.txt
También puede asegurarse de que está usando un shell Bash (o relacionado) escribiendo 'echo $ SHELL'. Creo que CSH y TCSH usan una semántica ligeramente diferente para este bucle.
Esto reduce el retraso introducido por la suspensión mientras sigue sondeando cada "x" segundos. Puede agregar más eventos si prevé que son necesarios.
+1 por eficiencia. Combinar con el sueño es feo. Para las personas que no conocen inotifywait, está en el paquete inotify-tools.
Michał Šrajer
7
Esa es una herramienta sumamente útil. Para cualquiera que se pregunte por qué el bucle, es para lidiar con posibles condiciones de carrera entre la creación y la espera y porque inotifywait tiene --excludeque filtrar los nombres de archivo, pero no --includeignorar todo excepto el nombre del archivo. El comando anterior debería usar el -qqargumento en lugar de >&/dev/nullaunque.
Craig Ringer
No es la --timeoutfrecuencia de verificación, ¿no? El punto de inotifywait es que no hay votación
Alex Dean
1
@AlexDean El tiempo de espera es para evitar una condición de carrera. El sondeo con suspensión es lento porque el bucle no se cerrará durante la suspensión, pero inotifywait saldrá antes del tiempo de espera si ve un evento.
publicado el
1
@AlexDean Sí, pero es necesario para evitar una condición de carrera TOCTTOU. De lo contrario, inotifywaitpodría bloquearse indefinidamente si se crea un archivo justo antes de que comience a escuchar eventos.
Publicado el
4
Tuve el mismo problema, pon el! fuera de los corchetes;
while![-f /tmp/list.txt ];do
echo "#"
sleep 1done
Además, si agrega un eco dentro del bucle, le dirá si está entrando en el bucle o no.
Me encontré con un problema similar y me llevó aquí, así que solo quería dejar mi solución para cualquiera que experimente lo mismo.
Descubrí que si ejecutaba, cat /tmp/list.txtel archivo estaría vacío, aunque estaba seguro de que el contenido se colocaba inmediatamente en el archivo. Resulta que si pongo un sleep 1;justo antes del cat /tmp/list.txt, funcionó como se esperaba. Debe haber habido un retraso entre el momento en que se creó el archivo y el momento en que se escribió, o algo por el estilo.
Como @ zane-hooper, tuve un problema similar en NFS. En sistemas de archivos paralelos / distribuidos, el retraso entre la creación de un archivo en una máquina y la otra máquina "viendo" puede ser muy grande, por lo que podría esperar hasta un minuto completo después de la creación del archivo antes de que salga el ciclo while (y también hay un efecto secundario de "ver" un archivo ya eliminado).
Esto crea la ilusión de que el script "no funciona" , mientras que en realidad es el sistema de archivos el que está dejando caer la pelota.
Me tomó un tiempo darme cuenta, espero que le ahorre a alguien algo de tiempo.
PD: Esto también causa una cantidad molesta de errores de "manejador de archivos obsoletos".
Aquí hay una versión con un tiempo de espera para que después de un período de tiempo el ciclo termine con un error:
# After 60 seconds the loop will exit
timeout=60while[!-f /tmp/list.txt ];do# When the timeout is equal to zero, show an error and leave the loop.if["$timeout"==0];then
echo "ERROR: Timeout while waiting for the file /tmp/list.txt."
exit 1fi
sleep 1# Decrease the timeout of one((timeout--))done
Respuestas:
Cuando dice "no funciona", ¿cómo sabe que no funciona?
Puede intentar averiguar si el archivo realmente existe agregando:
También puede asegurarse de que está usando un shell Bash (o relacionado) escribiendo 'echo $ SHELL'. Creo que CSH y TCSH usan una semántica ligeramente diferente para este bucle.
fuente
while [ ! -f /tmp/list.txt ]; do sleep 2; done; ls -l /tmp/list.txt
Si está en linux y tiene inotify-tools instaladas, puede hacer esto:
Esto reduce el retraso introducido por la suspensión mientras sigue sondeando cada "x" segundos. Puede agregar más eventos si prevé que son necesarios.
fuente
--exclude
que filtrar los nombres de archivo, pero no--include
ignorar todo excepto el nombre del archivo. El comando anterior debería usar el-qq
argumento en lugar de>&/dev/null
aunque.--timeout
frecuencia de verificación, ¿no? El punto de inotifywait es que no hay votacióninotifywait
podría bloquearse indefinidamente si se crea un archivo justo antes de que comience a escuchar eventos.Tuve el mismo problema, pon el! fuera de los corchetes;
Además, si agrega un eco dentro del bucle, le dirá si está entrando en el bucle o no.
fuente
Me encontré con un problema similar y me llevó aquí, así que solo quería dejar mi solución para cualquiera que experimente lo mismo.
Descubrí que si ejecutaba,
cat /tmp/list.txt
el archivo estaría vacío, aunque estaba seguro de que el contenido se colocaba inmediatamente en el archivo. Resulta que si pongo unsleep 1;
justo antes delcat /tmp/list.txt
, funcionó como se esperaba. Debe haber habido un retraso entre el momento en que se creó el archivo y el momento en que se escribió, o algo por el estilo.Mi código final:
¡Espero que esto ayude a alguien a ahorrar una media hora frustrante!
fuente
Como @ zane-hooper, tuve un problema similar en NFS. En sistemas de archivos paralelos / distribuidos, el retraso entre la creación de un archivo en una máquina y la otra máquina "viendo" puede ser muy grande, por lo que podría esperar hasta un minuto completo después de la creación del archivo antes de que salga el ciclo while (y también hay un efecto secundario de "ver" un archivo ya eliminado).
Esto crea la ilusión de que el script "no funciona" , mientras que en realidad es el sistema de archivos el que está dejando caer la pelota.
Me tomó un tiempo darme cuenta, espero que le ahorre a alguien algo de tiempo.
PD: Esto también causa una cantidad molesta de errores de "manejador de archivos obsoletos".
fuente
funciona con bash y sh ambos:
fuente
Aquí hay una versión con un tiempo de espera para que después de un período de tiempo el ciclo termine con un error:
fuente
Hazlo asi
fuente