Zakończenie nieskończonej pętli

52

Mam polecenie, które chcę uruchamiać ponownie automatycznie za każdym razem, gdy się kończy, więc uruchomiłem coś takiego:

while [ 1 ]; do COMMAND; done;

ale jeśli nie mogę zatrzymać pętli, Ctrl-cto po prostu zabija, COMMANDa nie całą pętlę.

Jak mam osiągnąć coś podobnego, ale co mogę zatrzymać bez konieczności zamykania terminalu?

Howardh
źródło
11
Jeśli jestem zaskoczony, po prostu używam Ctrl-Z, aby zatrzymać zadanie, a następnie „kill% 1”, aby go zabić.
Paul Cager
3
Po prostu poczekaj ... Linus został zacytowany: „Wszyscy wiemy, że Linux jest świetny ... robi nieskończone pętle w 5 sekund.” - więc naprawdę… po prostu poczekaj jeszcze kilka sekund, powinien zakończyć.
lornix,
@PaulCager też dla mnie pracował! Dlaczego działa, gdy Ctrl-C nie działa?
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
@cirosantilli zabija zadanie zewnętrzne (bash „wrapper”). W niektórych sytuacjach nie zabije natychmiast „POLECENIA”, na przykład, jeśli będziesz w tle, może wymknąć się żywcem, nawet jeśli jego rodzic nie żyje. Ale pętla nie działa, i to jest ważna część.
orion

Odpowiedzi:

37

Sprawdź status wyjścia polecenia. Jeśli polecenie zostało zakończone sygnałem, kodem wyjścia będzie 128 + numer sygnału. Z dokumentacji online GNU dla bash :

Dla celów powłoki polecenie, które kończy działanie z zerowym statusem wyjścia, zakończyło się powodzeniem. Niezerowy status wyjścia wskazuje na awarię. Ten pozornie intuicyjny schemat jest używany, więc istnieje jeden dobrze określony sposób wskazywania sukcesu i różne sposoby wskazywania różnych trybów awarii. Kiedy polecenie kończy się na fatalnym sygnale, którego liczba to N, Bash używa wartości 128 + N jako statusu wyjścia.

POSIX określa również, że wartość polecenia zakończonego sygnałem jest większa niż 128, ale wydaje się, że nie określa dokładnej wartości, jak GNU:

Status wyjścia polecenia, które zakończyło się, ponieważ odebrał sygnał, zgłasza się jako większy niż 128.

Na przykład, jeśli przerwiesz polecenie Ctrl, kodem wyjścia będzie 130, ponieważ SIGINT jest sygnałem 2 w systemach Unix. Więc:

while [ 1 ]; do COMMAND; test $? -gt 128 && break; done
Kyle Jones
źródło
9
Należy wspomnieć, że nie jest to zagwarantowane, w rzeczywistości wiele aplikacji tego nie zrobi.
Chris Down
1
@Kyle Jones: czy możesz link do dokumentów POSIX / GNU, które o tym wspominają?
Ciro Santilli 15 改造 中心 法轮功 六四 事件
@cirosantilli Gotowe.
Kyle Jones
@KyleJones dzięki! Nadal w praktyce nie działa dla COMMAND = paplay alert.ogg, może dlatego, że paplayobsługuje sygnał?
Ciro Santilli 16 改造 中心 法轮功 六四 事件
@cirosantilli Tak, to jest powód. Jeśli proces obsługuje sygnał i kończy działanie, różni się to od procesu kończącego się nieobsługiwanym sygnałem.
Kyle Jones
48

Możesz zatrzymać pracę i umieścić ją w tle, gdy jest uruchomiona, używając ctrl+ z. Następnie możesz zabić swoją pracę za pomocą:

$ kill %1

Gdzie [1] jest twoim numerem pracy.

Jem
źródło
Zobacz także tę odpowiedź, aby uzyskać wyjaśnienia i więcej.
Skippy le Grand Gourou
2
Ta stosunkowo nowa odpowiedź po prostu działa. Trzeba być poddanym głosowaniu. +1
shivams,
Bardzo mi pomogłeś. Właśnie tego szukałem w tym pytaniu :)
Maximilian Ruta
18

Powiedziałbym, że najlepiej umieścić swoją nieskończoną pętlę w skrypcie i obsługiwać tam sygnały. Oto podstawowy punkt wyjścia . Jestem pewien, że będziesz chciał go zmodyfikować, aby pasował. Skrypt używa trapdo przechwytywania ctrl- c(lub SIGTERM), zabija polecenie (użyłem sleeptutaj jako testu) i kończy działanie.

cleanup ()
{
kill -s SIGTERM $!
exit 0
}

trap cleanup SIGINT SIGTERM

while [ 1 ]
do
    sleep 60 &
    wait $!
done
ephsmith
źródło
4
Miły. Oto jak wykorzystałem tę wskazówkę, aby automatycznie otoczyć pakiet netcat:trap "exit 0" SIGINT SIGTERM; while true; do netcat -l -p 3000; done
Douglas,
2
jeśli dodasz to trappodejście do tego samego skryptu (bash) z nieskończoną pętlą do zabicia, użyj $$zamiast $!(patrz tutaj )
nowy
15

Generalnie po prostu przytrzymuję Ctrl-C. Wcześniej czy później zarejestruje się pomiędzy COMMANDi tym samym zakończy whilepętlę. Może jest lepszy sposób.

jw013
źródło
1
Nie jestem pewien, dlaczego, ale zawodzi w przypadku niektórych poleceń, takich jak paplayplik 1s.
Ciro Santilli 15 改造 中心 法轮功 六四 事件
To zadziałało dla mnie
Aleksandr
To brutalna siła wszystkich rozwiązań tutaj. : /
markroxor
8

Jeśli uruchomisz z -enim bash , wyjdzie on na dowolny błąd:

#!/bin/bash -e
false # returns 1
echo This won't be printed
Przywróć Monikę
źródło
7

Dlaczego nie po prostu

while [ 1 ]; do COMMAND || break; done;

Lub gdy jest używany w skrypcie,

#!/bin/bash
while [ 1 ]; do
  # ctrl+c terminates COMMAND and exits the while loop
  # (assuming COMMAND responds to ctrl+c)
  COMMAND || break
done;
Dale Anderson
źródło
1
Bardzo eleganckie rozwiązanie. Ale czy nie zadziałałoby to tylko wtedy, gdy COMMAND zawsze zwraca status zakończenia pomyślnego zakończenia?
howardh
Tak @howardh, to prawda.
Dale Anderson,
3

Wolę inne rozwiązanie:

touch .runcmd; while [ -f ".runcmd" ]; do COMMAND; sleep 1; done

Aby zabić pętlę, po prostu wykonaj:

rm .runcmd && kill `pidof COMMAND`
M4tze
źródło
2
  1. Zawsze możesz zabić proces za pomocą jego PID, nie ma potrzeby zamykania terminala
  2. Jeśli chcesz uruchomić coś w nieskończonej pętli, np. Demona, najlepiej umieścić to w tle
  3. while : stworzy nieskończoną pętlę i zaoszczędzi ci pisania [ 1 ]

    while :; do COMMAND; done &

Spowoduje to wydrukowanie PID. Jeśli wyjdziesz z monitu przy użyciu, ctrl+dzadanie w tle nie zostanie zamknięte, a później możesz je zabić z dowolnego miejscakill PID

Jeśli zgubisz PID, możesz użyć pstree -pa $USERlub, pgrep -fl '.*PROCESS.*'aby go znaleźć

errant.info
źródło
0

Użyj trap-

exit_()
{
    exit
}

while true
do
    echo 'running..'
    trap exit_ int
done
markroxor
źródło