Eine Endlosschleife beenden

52

Ich habe einen Befehl, den ich jedes Mal, wenn er beendet wird, automatisch erneut ausführen lassen möchte. Daher habe ich Folgendes ausgeführt:

while [ 1 ]; do COMMAND; done;

aber wenn ich die Schleife nicht stoppen kann Ctrl-c, tötet das einfach COMMANDund nicht die gesamte Schleife.

Wie würde ich etwas Ähnliches erreichen, das ich aber beenden kann, ohne das Terminal schließen zu müssen?

howardh
quelle
11
Wenn ich in der Bash bin, benutze ich einfach Strg-Z, um den Job zu stoppen und dann "% 1 töten", um ihn zu beenden.
Paul Cager
3
Warten Sie ... Linus wurde mit den Worten zitiert: "Wir alle wissen, dass Linux großartig ist ... es führt in 5 Sekunden Endlosschleifen aus." - also wirklich ... warten Sie noch ein paar Sekunden, es sollte abgeschlossen sein.
Lornix
@PaulCager hat auch für mich gearbeitet! Warum funktioniert es wo Ctrl-C nicht?
Ciro Santilli 事件 新疆 改造 法轮功 六四
@cirosantilli es tötet den äußeren Job (die Bash "Wrapper"). In einigen Situationen wird das "BEFEHL" nicht sofort beendet. Wenn Sie es beispielsweise im Hintergrund anzeigen, kann es auch dann lebend vorbeischleichen, wenn sein Elternteil tot ist. Aber die Schleife ist tot, und das ist der wichtige Teil.
Orion

Antworten:

37

Überprüfen Sie den Beendigungsstatus des Befehls. Wenn der Befehl durch ein Signal beendet wurde, ist der Beendigungscode 128 + die Signalnummer. Aus der GNU Online-Dokumentation für Bash :

Für die Zwecke der Shell ist ein Befehl erfolgreich, der mit dem Beendigungsstatus Null beendet wird. Ein Ausgangsstatus ungleich Null zeigt einen Fehler an. Dieses scheinbar kontraintuitive Schema wird verwendet, sodass es einen genau definierten Weg gibt, um den Erfolg anzuzeigen, und eine Vielzahl von Wegen, um verschiedene Fehlermodi anzuzeigen. Wenn ein Befehl bei einem schwerwiegenden Signal mit der Nummer N beendet wird, verwendet Bash den Wert 128 + N als Beendigungsstatus.

POSIX gibt auch an, dass der Wert eines Befehls, der durch ein Signal beendet wird, größer als 128 ist, aber den genauen Wert nicht wie GNU anzugeben scheint:

Der Beendigungsstatus eines Befehls, der beendet wurde, weil er ein Signal empfangen hat, wird als größer als 128 gemeldet.

Wenn Sie zum Beispiel einen Befehl mit Control-C unterbrechen, lautet der Exit-Code 130, da SIGINT auf Unix-Systemen Signal 2 ist. Damit:

while [ 1 ]; do COMMAND; test $? -gt 128 && break; done
Kyle Jones
quelle
9
Es sollte erwähnt werden, dass dies nicht garantiert ist, in der Tat werden viele Anwendungen dies nicht tun.
Chris Down
1
@Kyle Jones: Können Sie einen Link zu den POSIX / GNU-Dokumenten erstellen, in denen das erwähnt wird?
Ciro Santilli 新疆 14 改造 法轮功 六四
@cirosantilli Fertig
Kyle Jones
@KyleJones danke! Funktioniert das in der Praxis immer noch nicht für COMMAND = paplay alert.ogg, vielleicht behandelt da paplaydas Signal?
Ciro Santilli am
@cirosantilli Ja, das ist der Grund. Wenn ein Prozess das Signal verarbeitet und beendet wird, unterscheidet sich dies von dem Prozess, der durch ein nicht behandeltes Signal beendet wird.
Kyle Jones
48

Sie können den Job anhalten und mit ctrl+ in den Hintergrund stellen, während er ausgeführt wird z. Dann können Sie Ihren Job töten mit:

$ kill %1

Wobei [1] Ihre Auftragsnummer ist.

Jem
quelle
Siehe auch diese Antwort für Erklärungen und mehr.
Skippy le Grand Gourou
2
Diese relativ neue Antwort funktioniert einfach. Muss upvoted werden. +1
Shivams
Sie haben mir sehr geholfen. Dies ist, was ich in dieser Frage gesucht habe :)
Maximilian Ruta
18

Ich würde sagen, es ist am besten, wenn Sie Ihre Endlosschleife in ein Skript einfügen und dort mit Signalen umgehen. Hier ist ein grundlegender Ausgangspunkt . Ich bin sicher, dass Sie es an Ihre Bedürfnisse anpassen möchten. Das Skript verwendet trap, um ctrl- c(oder SIGTERM) zu fangen , den Befehl abzubrechen (den ich sleephier als Test verwendet habe) und zu beenden.

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

trap cleanup SIGINT SIGTERM

while [ 1 ]
do
    sleep 60 &
    wait $!
done
Ephsmith
quelle
4
Nett. So habe ich diesen Tipp verwendet, um einen Netcat-Wrapper für den automatischen Neustart zu erstellen:trap "exit 0" SIGINT SIGTERM; while true; do netcat -l -p 3000; done
Douglas,
2
wenn Sie diesen Add trapAnsatz auf die gleiche (bash) Skript mit der Endlos - Schleife getötet werden, verwenden $$statt $!(siehe hier )
ardnew
15

Ich halte im Allgemeinen nur gedrückt Ctrl-C. Früher oder später wird es sich zwischen registrieren COMMANDund somit die whileSchleife beenden . Vielleicht gibt es einen besseren Weg.

jw013
quelle
1
Ich weiß nicht warum, aber es schlägt bei bestimmten Befehlen fehl, wie paplaybei einer 1s-Datei.
Ciro Santilli 新疆 14 改造 法轮功 六四
Es hat bei mir funktioniert
alexandr
Das ist die rohe Kraft aller Lösungen hier. : /
Markroxor
8

Wenn Sie bash mit -ediesem Befehl ausführen, wird das Programm unter folgenden Fehlerbedingungen beendet:

#!/bin/bash -e
false # returns 1
echo This won't be printed
Setzen Sie Monica wieder ein
quelle
7

Warum nicht einfach,

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

Oder wenn in einem Skript verwendet,

#!/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
quelle
1
Sehr elegante Lösung. Aber würde das nicht nur funktionieren, wenn COMMAND immer einen erfolgreichen Exit-Status zurückgibt?
howardh
Ja @howardh, das ist richtig.
Dale Anderson
3

Ich bevorzuge eine andere Lösung:

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

Um die Schleife zu beenden, machen Sie einfach Folgendes:

rm .runcmd && kill `pidof COMMAND`
M4tze
quelle
2
  1. Sie können einen Prozess jederzeit über seine PID beenden. Sie müssen Ihr Terminal nicht schließen
  2. Wenn Sie etwas wie einen Daemon in einer Endlosschleife ausführen möchten, sollten Sie es in den Hintergrund stellen
  3. while : erstellt eine Endlosschleife und erspart Ihnen das Schreiben der [ 1 ]

    while :; do COMMAND; done &

Dadurch wird die PID gedruckt. Wenn Sie Ihre Eingabeaufforderung mit ctrl+dbeenden, wird der Hintergrundjob nicht beendet, und Sie können den Job später von überall mit beendenkill PID

Wenn Sie die Übersicht über Ihre PID verlieren, können Sie pstree -pa $USERoder verwenden pgrep -fl '.*PROCESS.*', um sie zu finden

errant.info
quelle
0

Verwenden Sie trap-

exit_()
{
    exit
}

while true
do
    echo 'running..'
    trap exit_ int
done
Markroxor
quelle