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?


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

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.


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

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


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

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;

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`

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źć


0

Użyj trap-

exit_()
{
    exit
}

while true
do
    echo 'running..'
    trap exit_ int
done
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.