Pętla While, aby sprawdzić, czy plik istnieje w bash

97

Pracuję nad skryptem powłoki, który wprowadza pewne zmiany w pliku txt tylko wtedy, gdy istnieje, jednak ta pętla testowa nie działa, zastanawiam się, dlaczego? Dziękuję Ci!

while [ ! -f /tmp/list.txt ] ;
do
      sleep 2
done
Zenet
źródło
3
Nie mogę powiedzieć, że jestem zaskoczony; ta pętla nie próbuje niczego zmienić.
Ignacio Vazquez-Abrams
Średnik jest zbędny. W jaki sposób ta pętla testowa nie działa? Będzie iteracyjnie spać przez 2 sekundy, aż do powstania pliku /tmp/list.txt.
Jonathan Leffler
5
U mnie działa - pętla kończy się, gdy plik jest tworzony poza skryptem.
1
w rzeczywistości ta pętla służy tylko do czekania, aż plik się tam znajdzie, reszta mojego skryptu robi zmiany ...: p
Zenet
1
Wtedy pętla while działa, to tylko ja ... przepraszam.
Zenet

Odpowiedzi:

149

Kiedy mówisz „nie działa”, skąd wiesz, że to nie działa?

Możesz spróbować dowiedzieć się, czy plik faktycznie istnieje, dodając:

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

Możesz także upewnić się, że używasz powłoki Bash (lub pokrewnej), wpisując „echo $ SHELL”. Myślę, że CSH i TCSH używają nieco innej semantyki dla tej pętli.

CWF
źródło
Dlaczego używasz odwróconego sprawdzania plików? Czy nie należy zamiast tego użyć [-f /tmp/list.txt]?
walentynki
2
@valentt Żadna pętla hew nie mówi dosłownie „gdy NIE istnieje plik, śpij” .. jeśli usuniesz „NIE”, pętla przerwie się natychmiast
Kenyakorn Ketsombut
2
w 1 linii:while [ ! -f /tmp/list.txt ]; do sleep 2; done; ls -l /tmp/list.txt
DrumM
55

Jeśli korzystasz z Linuksa i masz zainstalowane narzędzia inotify-tools, możesz to zrobić:

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

Zmniejsza to opóźnienie wprowadzane przez uśpienie, podczas gdy nadal odpytywanie co „x” sekund. Możesz dodać więcej wydarzeń, jeśli przewidujesz, że są potrzebne.

Yingted
źródło
4
+1 za efektywność. Kąpiel podczas snu jest brzydka. Dla ludzi, którzy nie znają inotifywait - jest w pakiecie inotify-tools.
Michał Šrajer
7
To niezwykle przydatne narzędzie. Dla każdego, kto zastanawia się, dlaczego pętla, ma do czynienia z możliwymi warunkami wyścigu między tworzeniem a oczekiwaniem oraz ponieważ inotifywait musi --excludeodfiltrować nazwy plików, ale nie --includeignorować wszystkiego oprócz nazwy pliku. Powyższe polecenie powinno -qqzamiast tego używać argumentu >&/dev/null.
Craig Ringer
t to --timeout, a nie częstotliwość sprawdzania, nie? Celem inotifywait jest to, że nie ma głosowania
Alex Dean
1
@AlexDean Limit czasu ma zapobiec wystąpieniu sytuacji wyścigu. Sondowanie z usypianiem jest powolne, ponieważ pętla nie kończy się podczas uśpienia, ale inotifywait zakończy działanie przed upływem limitu czasu, jeśli zobaczy zdarzenie.
wysłano
1
@AlexDean Tak, ale jest to konieczne, aby zapobiec sytuacji wyścigu TOCTTOU. W przeciwnym razie inotifywaitmoże zawiesić się na czas nieokreślony, jeśli plik zostanie utworzony tuż przed rozpoczęciem nasłuchiwania zdarzeń.
wysłano
4

Miałem ten sam problem, włóż! poza nawiasami;

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

Ponadto, jeśli dodasz echo wewnątrz pętli, powie ci, czy wchodzisz w pętlę, czy nie.

David Cox
źródło
2

Natknąłem się na podobny problem i doprowadził mnie tutaj, więc chciałem zostawić moje rozwiązanie każdemu, kto doświadcza tego samego.

Zauważyłem, że jeśli uruchomię, cat /tmp/list.txtplik będzie pusty, mimo że byłem pewien, że zawartość jest natychmiast umieszczana w pliku. Okazuje się, że jeśli wstawię sleep 1;tuż przed cat /tmp/list.txt, zadziałało zgodnie z oczekiwaniami. Musiało być opóźnienie między utworzeniem pliku a jego zapisaniem lub czymś podobnym.

Mój ostateczny kod:

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

Mam nadzieję, że pomoże to komuś zaoszczędzić frustrujące pół godziny!

Zane
źródło
1

Podobnie jak @ zane-hooper, miałem podobny problem na NFS. Na równoległych / rozproszonych systemach plików opóźnienie między utworzeniem pliku na jednej maszynie a „zobaczeniem” go na drugiej maszynie może być bardzo duże, więc mogłem odczekać do pełnej minuty po utworzeniu pliku, zanim pętla while zakończy działanie (i istnieje również skutek „zobaczenia” już usuniętego pliku).

To stwarza iluzję, że scenariusz „nie działa” , podczas gdy w rzeczywistości jest to system plików, który jest upuszczenie kuli.

Zajęło mi to trochę czasu, mam nadzieję, że to komuś zaoszczędzi trochę czasu.

PS Powoduje to również irytującą liczbę błędów „Stale obsługi plików”.

Ruslan Shaydulin
źródło
0

działa z bash i sh zarówno:

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
źródło
0

Oto wersja z limitem czasu, więc po pewnym czasie pętla kończy się błędem:

# 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
źródło
-3

zrób to w ten sposób

while true
do
  [ -f /tmp/list.txt ] && break
  sleep 2
done
ls -l /tmp/list.txt
ghostdog74
źródło