Boucle While pour tester si un fichier existe dans bash

94

Je travaille sur un script shell qui effectue certaines modifications sur un fichier txt uniquement s'il existe, mais cette boucle de test ne fonctionne pas, je me demande pourquoi? Je vous remercie!

while [ ! -f /tmp/list.txt ] ;
do
      sleep 2
done
Zenet
la source
3
Je ne peux pas dire que je suis surpris; cette boucle n'essaye pas de changer quoi que ce soit.
Ignacio Vazquez-Abrams
Le point-virgule est redondant. De quelle manière cette boucle de test ne fonctionne-t-elle pas? Il dormira itérativement pendant 2 secondes jusqu'à ce que le fichier /tmp/list.txt existe.
Jonathan Leffler
5
Fonctionne pour moi - la boucle se termine lorsque le fichier est créé en dehors du script.
1
en fait, cette boucle ne sert qu'à attendre que le fichier soit là, le reste de mon script fait les changements ...: p
Zenet
1
Ensuite, la boucle while fonctionne, c'est juste moi ... désolé.
Zenet

Réponses:

145

Quand vous dites "ne fonctionne pas", comment savez-vous que cela ne fonctionne pas?

Vous pouvez essayer de déterminer si le fichier existe réellement en ajoutant:

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

Vous pouvez également vous assurer que vous utilisez un shell Bash (ou associé) en tapant «echo $ SHELL». Je pense que CSH et TCSH utilisent une sémantique légèrement différente pour cette boucle.

CWF
la source
Pourquoi utilisez-vous la vérification des fichiers inversés? Ne devrait-on pas utiliser while [-f /tmp/list.txt] à la place?
valentt
2
@valentt Aucune boucle hew dit littéralement "alors que le fichier NOT existe, dormez" .. si vous supprimiez le 'NON', la boucle se briserait instantanément
Kenyakorn Ketsombut
2
en 1 ligne:while [ ! -f /tmp/list.txt ]; do sleep 2; done; ls -l /tmp/list.txt
DrumM
55

Si vous êtes sous Linux et avez installé inotify-tools, vous pouvez le faire:

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

Cela réduit le délai introduit par le sommeil tout en continuant d'interroger toutes les "x" secondes. Vous pouvez ajouter d'autres événements si vous prévoyez qu'ils sont nécessaires.

yingted
la source
4
+1 pour l'efficacité. Mettre en commun avec le sommeil est laid. Pour les personnes ne connaissant pas inotifywait - il est dans le package inotify-tools.
Michał Šrajer
7
C'est un outil extrêmement pratique. Pour quiconque se demande pourquoi la boucle, c'est pour faire face aux conditions de concurrence possibles entre la création et l'attente et parce que inotifywait doit --excludefiltrer les noms de fichiers, mais pas --includetout ignorer sauf le nom de fichier. La commande ci-dessus devrait utiliser l' -qqargument au lieu de >&/dev/nullcependant.
Craig Ringer
t est le --timeout, pas la fréquence de vérification, non? Le point d'inotifywait est qu'il n'y a pas de sondage
Alex Dean
1
@AlexDean Le délai d'expiration permet d'éviter une condition de concurrence. L'interrogation avec sleep est lente car la boucle ne se terminera pas pendant le sommeil, mais inotifywait se fermera avant l'expiration du délai s'il voit un événement.
yingted
1
@AlexDean Oui, mais il est nécessaire d'éviter une condition de concurrence TOCTTOU. Sinon, inotifywaitpourrait se bloquer indéfiniment si un fichier est créé juste avant de commencer à écouter les événements.
yingted
4

J'ai eu le même problème, mettez le! en dehors des crochets;

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

De plus, si vous ajoutez un écho à l'intérieur de la boucle, il vous dira si vous entrez dans la boucle ou non.

David Cox
la source
2

J'ai rencontré un problème similaire et cela m'a conduit ici, alors je voulais juste laisser ma solution à tous ceux qui vivent la même chose.

J'ai trouvé que si j'exécutais cat /tmp/list.txtle fichier serait vide, même si j'étais certain que le contenu était placé immédiatement dans le fichier. Il s'avère que si je mets un sleep 1;juste avant lecat /tmp/list.txt cela a fonctionné comme prévu. Il doit y avoir eu un délai entre le moment où le fichier a été créé et le moment où il a été écrit, ou quelque chose du genre.

Mon code final:

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

J'espère que cela aidera quelqu'un à gagner une demi-heure frustrante!

Zane Hooper
la source
1

Comme @ zane-hooper, j'ai eu un problème similaire sur NFS. Sur les systèmes de fichiers parallèles / distribués, le décalage entre la création d'un fichier sur une machine et la "visualisation" de l'autre machine peut être très important, donc je pourrais attendre jusqu'à une minute complète après la création du fichier avant que la boucle while se termine (et il y a aussi une séquelle de "voir" un fichier déjà supprimé).

Cela crée l'illusion que le script "ne fonctionne pas" , alors qu'en fait c'est le système de fichiers qui lâche la balle.

Cela m'a pris un certain temps à comprendre, j'espère que cela fera gagner du temps à quelqu'un.

PS Cela provoque également un nombre ennuyeux d'erreurs "Stale file handler".

Ruslan Shaydulin
la source
0

fonctionne avec bash et sh à la fois:

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
la source
0

Voici une version avec un timeout pour qu'après un certain temps la boucle se termine par une erreur:

# 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
la source
-3

fais-le comme ça

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