Viele Beispiele für die trap
Verwendung trap ... INT TERM EXIT
für Bereinigungsaufgaben. Aber ist es wirklich notwendig, alle drei Zeichen aufzuzählen?
Das Handbuch sagt:
Wenn ein SIGNAL_SPEC EXIT (0) ist, wird ARG beim Verlassen der Shell ausgeführt.
was meines erachtens zutrifft, ob das script normal beendet wurde oder weil es empfangen wurde SIGINT
oder SIGTERM
. Ein Experiment bestätigt auch meine Überzeugung:
$ cat ./trap-exit
#!/bin/bash
trap 'echo TRAP' EXIT
sleep 3
$ ./trap-exit & sleep 1; kill -INT %1
[1] 759
TRAP
[1]+ Interrupt ./trap-exit
$ ./trap-exit & sleep 1; kill -TERM %1
[1] 773
TRAP
[1]+ Terminated ./trap-exit
Warum listen dann so viele Beispiele alle auf INT TERM EXIT
? Oder habe ich etwas verpasst und gibt es einen Fall, in dem eine Sohle EXIT
fehlen würde?
INT TERM EXIT
der Bereinigungscode zweimal ausgeführt wird, wennSIGTERM
oderSIGINT
empfangen wird.Antworten:
Die POSIX-Spezifikation sagt nicht viel über die Bedingungen aus, die zur Ausführung des EXIT-Traps führen, sondern nur darüber, wie seine Umgebung bei der Ausführung aussehen muss.
In der Ascheschale von Busybox gibt Ihr Trap-Exit-Test vor dem Beenden aufgrund von SIGINT oder SIGTERM kein "TRAP" zurück. Ich würde vermuten, dass es noch andere Muscheln gibt, die möglicherweise nicht so funktionieren.
quelle
dash
fängt auch nicht erst an,EXIT
wenn es eingehtSIGINT/SIGTERM
.zsh
auch - also vielleichtbash
die einzige Shell, in derEXIT
auch Signale übereinstimmen.zsh
fängt nicht an,EXIT
wenn es empfängtINT
, aber es tut, wenn es empfängtTERM
. EDIT: Ich habe gerade bemerkt, wie alt das war ...Ja, da gibt es einen Unterschied.
Dieses Skript wird beendet, wenn Sie auf drücken Enteroder es senden
SIGINT
oderSIGTERM
:Dieses Skript wird beendet, wenn Sie Folgendes drücken Enter:
* Getestet in sh , Bash und Zsh . (Funktioniert nicht mehr in sh, wenn Sie einen Befehl zum Ausführen von trap hinzufügen.)
Es gibt auch das, was @Shawn gesagt hat: Ash und Dash fangen keine Signale mit ein
EXIT
.Um mit Signalen robust umzugehen, ist es am besten, die Überfüllung
EXIT
ganz zu vermeiden und so etwas wie das Folgende zu verwenden:quelle
mktemp
Aufrufen geworden.exit
nötigcleanup
?ERR
, um damit umzugehen , aber es ist nicht portabel .trap - INT TERM; kill -2 $$
als letzte Zeile der Bereinigung der übergeordneten Shell mitteilen, dass sie vorzeitig beendet wurde. Wenn eine übergeordnete Shell foobar.sh Ihr Skript (foo.sh) und dann bar.sh aufruft, soll bar.sh nicht ausgeführt werden, wenn INT / TERM an foo.sh gesendet wird.trap cleanup EXIT
wird diese Weitergabe automatisch verarbeiten, daher ist es IMO das robusteste. Es bedeutet auch, dass Sie nichtcleanup
am Ende des Skripts aufrufen müssen .Verfeinerung der letzten Antwort, da es Probleme gibt:
Punkte oben:
INT- und TERM-Handler werden beim Testen nicht beendet. Sie behandeln den Fehler, und die Shell kehrt zum Beenden zurück (was nicht allzu überraschend ist). So stelle ich sicher, dass die Bereinigung danach beendet wird und im Fall der Signale immer einen Fehlercode verwendet (und im anderen Fall eines normalen Exits den Fehlercode beibehält).
Mit bash scheint es, dass das Beenden im INT-Handler auch den EXIT-Handler aufruft. Daher entpacke ich den Exit-Handler und rufe ihn selbst auf (was in jeder Shell unabhängig vom Verhalten funktioniert).
Ich fange exit ein, weil Shell-Skripte beendet werden können, bevor sie das Ende erreichen - Syntaxfehler, set -e und eine Rückkehr ungleich Null, einfach exit aufrufen. Man kann sich nicht darauf verlassen, dass ein Muschelskript auf den Grund geht.
SIGQUIT ist Strg- \, wenn Sie es noch nie versucht haben. Erhältst du einen Bonus Coredump. Ich denke, es lohnt sich auch zu fangen, auch wenn es etwas dunkel ist.
Wenn Sie (wie ich) in der Vergangenheit immer mehrmals die Tastenkombination Strg-C drücken, wird dies manchmal in der Mitte des Bereinigungsteils Ihres Shell-Skripts angezeigt. Dies funktioniert jedoch nicht immer so perfekt, wie Sie es möchten.
quelle
trap
den Anrufer 130 für SIGINT, 143 für SIGTERM bekommen würde usw. Also ich würde erfassen und den richtigen Exit - Code passieren , wie:sig_cleanup() { err=$?; trap '' EXIT; (exit $err); cleanup; }
.trap '' EXIT INT TERM
in der Bereinigungsfunktion klären ? Damit soll eine versehentliche Unterbrechung der Bereinigung durch den Benutzer verhindert werden, die Sie im letzten Absatz erwähnt haben? Ist das nichtEXIT
überflüssig?