Ciclo while per verificare se esiste un file in bash


94

Sto lavorando su uno script di shell che apporta alcune modifiche su un file txt solo se esiste, tuttavia questo ciclo di test non funziona, mi chiedo perché? Grazie!

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

3
Non posso dire di essere sorpreso; quel ciclo non tenta di cambiare nulla.
Ignacio Vazquez-Abrams

Il punto e virgola è ridondante. In che modo quel ciclo di test non funziona? Si fermerà iterativamente per 2 secondi fino a quando il file /tmp/list.txt non esiste.
Jonathan Leffler

5
Funziona per me: il ciclo termina quando il file viene creato al di fuori dello script.

1
in effetti, questo ciclo serve solo ad aspettare che il file sia lì, il resto del mio script fa le modifiche ...: p
Zenet

1
Quindi il ciclo while funziona, sono solo io ... scusa.
Zenet

Risposte:


145

Quando dici "non funziona", come fai a sapere che non funziona?

Potresti provare a capire se il file esiste effettivamente aggiungendo:

while [ ! -f /tmp/list.txt ]
do
  sleep 2 # or less like 0.2
done
ls -l /tmp/list.txt

Potresti anche assicurarti di utilizzare una shell Bash (o relativa) digitando "echo $ SHELL". Penso che CSH e TCSH utilizzino una semantica leggermente diversa per questo ciclo.


Perché stai usando il controllo dei file invertiti? Invece non dovrebbe essere usato [-f /tmp/list.txt]?
valentt

2
@valentt No hew loop dice letteralmente "mentre il file NON esiste, dormi" .. se rimuovi il 'NOT' il ciclo si interromperà istantaneamente
Kenyakorn Ketsombut

2
in 1 riga:while [ ! -f /tmp/list.txt ]; do sleep 2; done; ls -l /tmp/list.txt
DrumM

55

Se sei su Linux e hai installato inotify-tools, puoi farlo:

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

Ciò riduce il ritardo introdotto dallo stato di sospensione durante il polling ogni "x" secondi. Puoi aggiungere più eventi se prevedi che siano necessari.


4
+1 per l'efficienza. Fare il bagno con il sonno è brutto. Per le persone che non conoscono inotifywait, è nel pacchetto inotify-tools.
Michał Šrajer

7
È uno strumento estremamente utile. Per chiunque si chieda perché il ciclo, è quello di affrontare possibili condizioni di competizione tra la creazione e l'attesa e perché inotifywait deve --excludefiltrare i nomi dei file, ma non --includeignorare tutto tranne il nome del file. Il comando precedente dovrebbe utilizzare l' -qqargomento invece di >&/dev/nullsebbene.
Craig Ringer

non è la --timeoutfrequenza del controllo, no? Il punto di inotifywait è che non ci sono sondaggi
Alex Dean

1
@AlexDean Il timeout serve a prevenire una condizione di competizione. Il polling con sleep è lento perché il ciclo non si chiude durante lo sleep, ma inotifywait uscirà prima del timeout se vede un evento.
yingted

1
@AlexDean Sì, ma è necessario prevenire una race condition TOCTTOU. In caso contrario, inotifywaitpotrebbe bloccarsi indefinitamente se un file viene creato appena prima che inizi ad ascoltare gli eventi.
yingted

4

Ho avuto lo stesso problema, metti il! fuori dalle parentesi;

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

Inoltre, se aggiungi un eco all'interno del loop, ti dirà se stai entrando nel loop o meno.


2

Mi sono imbattuto in un problema simile e mi ha portato qui, quindi volevo lasciare la mia soluzione a chiunque provasse la stessa cosa.

Ho scoperto che se avessi eseguito cat /tmp/list.txtil file sarebbe stato vuoto, anche se ero certo che ci fossero contenuti che venivano inseriti immediatamente nel file. Risulta che se metto un sleep 1;appena prima cat /tmp/list.txtche ha funzionato come previsto. Deve esserci stato un ritardo tra il momento in cui il file è stato creato e il momento in cui è stato scritto, o qualcosa del genere.

Il mio codice finale:

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

Spero che questo aiuti a risparmiare a qualcuno una frustrante mezz'ora!


1

Come @ zane-hooper, ho avuto un problema simile su NFS. Su filesystem paralleli / distribuiti il ​​ritardo tra la creazione di un file su una macchina e il "vederlo" sull'altra macchina può essere molto grande, quindi potrei aspettare fino a un intero minuto dopo la creazione del file prima che il ciclo while termini (e c'è anche un effetto collaterale di "vedere" un file già cancellato).

Questo crea l'illusione che la sceneggiatura "non funzioni" , mentre in realtà è il filesystem a far cadere la palla.

Mi ci è voluto un po 'per capirlo, spero che faccia risparmiare tempo a qualcuno.

PS Questo causa anche un numero fastidioso di errori "Stale file handler".


0

funziona con bash e sh entrambi:

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

0

Ecco una versione con un timeout in modo che dopo un certo periodo di tempo il ciclo termini con un errore:

# 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

-3

Fai cosi

while true
do
  [ -f /tmp/list.txt ] && break
  sleep 2
done
ls -l /tmp/list.txt
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.