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!

while [ ! -f /tmp/list.txt ] ;
do
      sleep 2
done
Zenet
fuente
3
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.2
done
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.

CWF
fuente
¿Por qué está utilizando la verificación de archivos invertida? ¿No debería usarse while [-f /tmp/list.txt] en su lugar?
valentt
2
@valentt No hew loop diga literalmente "mientras el archivo NO exista, duerma" ... si eliminara el 'NOT', el bucle se rompería instantáneamente
Kenyakorn Ketsombut
2
en 1 línea:while [ ! -f /tmp/list.txt ]; do sleep 2; done; ls -l /tmp/list.txt
DrumM
55

Si está en linux y tiene inotify-tools instaladas, puede hacer esto:

file=/tmp/list.txt
while [ ! -f "$file" ]
do
    inotifywait -qqt 2 -e create -e moved_to "$(dirname $file)"
done

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.

yingted
fuente
4
+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 1
done

Además, si agrega un eco dentro del bucle, le dirá si está entrando en el bucle o no.

David Cox
fuente
2

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.

Mi código final:

while [ ! -f /tmp/list.txt ];
do
    sleep 1;
done;
sleep 1;
cat /tmp/list.txt;

¡Espero que esto ayude a alguien a ahorrar una media hora frustrante!

Zane Hooper
fuente
1

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".

Ruslan Shaydulin
fuente
0

funciona con bash y sh ambos:

touch /tmp/testfile
sleep 10 && rm /tmp/testfile &
until ! [ -f /tmp/testfile ]
do
   echo "testfile still exist..."
   sleep 1
done
echo "now testfile is deleted.."
neerajmalve
fuente
0

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=60

while [ ! -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 1
  fi

  sleep 1

  # Decrease the timeout of one
  ((timeout--))
done
ZedTuX
fuente
-3

Hazlo asi

while true
do
  [ -f /tmp/list.txt ] && break
  sleep 2
done
ls -l /tmp/list.txt
ghostdog74
fuente