Jak powtórzyć pętlę n razy w Bash

9

Mam poniższy scenariusz:

if [file exists]; then
   exit   
elif
   recheck if file exist (max 10 times)
   if found exit else recheck again as per counter  
fi 
Rocky86
źródło
Jest to bardzo podstawowa funkcja w powłokach. Czy w ogóle to badałeś?
Peschke,
Tak. Ale nie otrzymuję oczekiwanych danych wyjściowych z mojego kodu. Chcę także pisać możliwie jak najkrócej
Rocky86,
1
@ Peschke, no cóż, potrzebowaliby co najmniej trzech podstawowych funkcji (pętle, warunkowe, testowanie pliku, wyjście z pętli). Przynajmniej pytanie jest dość jasne. Choć może zawierać szkic tego, co próbował Rocky, ale i tak ktoś przepisałby go w odpowiedziach. ;)
ilkkachu

Odpowiedzi:

9

Istnieje wiele sposobów wykonania tej pętli.

Ze ksh93składnią (obsługiwaną również przez zshi bash):

for (( i=0; i<10; ++i)); do
    [ -e filename ] && break
    sleep 10
done

Dla dowolnej powłoki podobnej do POSIX:

n=0
while [ "$n" -lt 10 ] && [ ! -e filename ]; do
    n=$(( n + 1 ))
    sleep 10
done

Obie pętle śpią po 10 sekund w każdej iteracji przed ponownym przetestowaniem istnienia pliku.

Po zakończeniu pętli będziesz musiał ostatni raz sprawdzić, czy plik istnieje, aby dowiedzieć się, czy pętla zakończyła się z powodu uruchomienia 10 razy, czy z powodu pojawienia się pliku.

Jeśli chcesz i masz dostęp do narzędzi inotify, możesz zastąpić sleep 10połączenie

inotifywait -q -t 10 -e create ./ >/dev/null

To czekałoby na wystąpienie zdarzenia tworzenia pliku w bieżącym katalogu, ale upłynęło 10 minut. W ten sposób pętla kończy się, gdy tylko pojawi się nazwa pliku (jeśli się pojawi).

Może wyglądać pełny kod z inotifywait(zamień na, sleep 10jeśli tego nie chcesz)

for (( i=0; i<10; ++i)); do
    [ -e filename ] && break
    inotifywait -q -t 10 -e create ./ >/dev/null
done

if [ -e filename ]; then
    echo 'file appeared!'
else
    echo 'file did not turn up in time'
fi
Kusalananda
źródło
Dzięki inotify możesz prawie zastąpić całą pętlę. Po prostu sprawdź, czy plik tam jest, a jeśli nie, inotifywait przez 100 sekund. Prawie, ponieważ plik można utworzyć tuż między testem a testem inotify, a ty musisz spać przez pełne 100 sekund, zanim
upłynie limit
1
@ilkkachu Tak, to dobry pomysł, ale tutaj używam tylko inotifywaitjako zamiennik drop-in sleep.
Kusalananda
8

Jeśli liczba nie jest zmienną, możesz użyć rozwinięcia nawiasu:

for i in {1..10}   # you can also use {0..9}
do
  whatever
done

Jeśli liczba jest zmienną, możesz użyć seqpolecenia:

count=10
for i in $(seq $count)
do
  whatever
done
ksenoid
źródło
Chcę zapętlać tylko wtedy, gdy plik nie zostanie znaleziony (maks. 10 razy). Jeśli znaleziono, powiedzmy, że trzeci raz, a następnie zakończ pomyślnie
Rocky86
@ Rocky86: Nie jest to sprzeczne z rozwiązaniem zaproponowanym przez ksenoid. Nikt nie zmusza Cię do liczenia do końca ....
user1934428,
Podoba mi się ten$(seq $count)
Worker
0
n=0
until [ "$((n+=1))" -gt 10 ]
do    <exists? command exit
done
echo oh noes!

choć test -e file && exitjest bardziej elastyczny

mikeserv
źródło
Dlaczego znak zapytania? Zauważ, że zachowanie globów w celu przekierowań różni się w zależności od powłoki.
Stéphane Chazelas,
2
Zauważ, że ma to efekt uboczny otwierania pliku, który na przykład dla fifos może być całkiem zły (gorzej z dowiązaniem symbolicznym do / dev / watchdog w Linuksie na przykład)
Stéphane Chazelas
Nawet w Bash, gdzie szukałby takiego exists1lub podobnego pliku , nadal wyświetla wiele błędów, jeśli / kiedy nie znaleziono pasującego pliku. (Również błędy jeśli istnieje wiele meczów.) Każda inna powłoka Testowałem wydaje się dawać błędy w każdym razie ...
ilkkachu
@ikkachu - tak. o to właśnie chodziło. jeśli wystąpi błąd, skrypt zgłasza się. jeśli stderr powinien zostać stłumiony, stłum go done 2<>/dev/null. czy bashto jest skrypty? myślałem, że to działa tylko w -inieoperacyjnym kontekście. wciąż exists?jest tak samo jak nazwa wypełniacza jak file. ale tak, nienawidzę cytowania w przekierowaniach - jeśli to wszystko psuje.
mikeserv
@ Stéphane - naprawdę bez powodu. ale tak, fifos, nieczytelne ... dlatego zauważyłem test -e.
mikeserv