it-swarm.com.de

"trap ... INT TERM EXIT" wirklich notwendig?

Viele Beispiele für trap verwenden trap ... INT TERM EXIT für Bereinigungsaufgaben. Aber ist es wirklich notwendig, alle drei Sigspecs aufzulisten?

Das Handbuch sagt:

Wenn ein SIGNAL_SPEC EXIT (0) ist, wird ARG beim Beenden der Shell ausgeführt.

was meiner Meinung nach gilt, ob das Skript normal beendet wurde oder beendet wurde, weil es SIGINT oder SIGTERM erhalten hat. 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 INT TERM EXIT? Oder habe ich etwas verpasst und gibt es einen Fall, in dem ein einziges EXIT fehlen würde?

71
musiphil

Das 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 aussehen muss, wenn er ausgeführt wird.

In der Asche-Shell von Busybox gibt Ihr Trap-Exit-Test vor dem Beenden aufgrund von SIGINT oder SIGTERM kein "TRAP" wieder. Ich würde vermuten, dass es andere Muscheln gibt, die möglicherweise nicht so funktionieren.

# /tmp/test.sh & sleep 1; kill -INT %1
# 
[1]+  Interrupt                  /tmp/test.sh
# 
# 
# /tmp/test.sh & sleep 1; kill -TERM %1
# 
[1]+  Terminated                 /tmp/test.sh
# 
22
Shawn J. Goff

Ja, es gibt einen Unterschied.

Dieses Skript wird beendet, wenn Sie drücken Enteroder senden Sie es SIGINT oder SIGTERM:

trap '' EXIT
echo ' --- press ENTER to close --- '
read response

Dieses Skript wird beendet, wenn Sie drücken Enter::

trap '' EXIT INT TERM
echo ' --- press ENTER to close --- '
read response

* Getestet in sh , Bash und Zsh . (funktioniert nicht mehr in sh , wenn Sie einen Befehl zum Ausführen der Falle hinzufügen)


Es gibt auch das, was @Shawn gesagt hat: Ash und Dash fangen nicht ein Signale mit EXIT.

Um Signale robust zu handhaben, ist es am besten, das Einfangen von EXIT insgesamt zu vermeiden und Folgendes zu verwenden:

cleanup() {
    echo "Cleaning stuff up..."
    exit
}

trap cleanup INT TERM
echo ' --- press ENTER to close --- '
read var
cleanup
28
Zaz

Verfeinern Sie die letzte Antwort, weil sie Probleme hat:

# Our general exit handler
cleanup() {
    err=$?
    echo "Cleaning stuff up..."
    trap '' EXIT INT TERM
    exit $err 
}
sig_cleanup() {
    trap '' EXIT # some shells will call EXIT after the INT handler
    false # sets $?
    cleanup
}
trap cleanup EXIT
trap sig_cleanup INT QUIT TERM

Punkte oben:

INT- und TERM-Handler werden beim Testen nicht für mich beendet - sie behandeln den Fehler, dann kehrt die Shell zum Beenden zurück (und das ist nicht allzu überraschend). Daher stelle ich sicher, dass die Bereinigung danach beendet wird und bei den Signalen immer ein Fehlercode verwendet wird (und im anderen Fall bei einem normalen Beenden der Fehlercode erhalten bleibt).

Mit bash scheint es, dass das Beenden im INT-Handler auch den EXIT-Handler aufruft. Daher entferne ich den Exit-Handler und rufe ihn selbst auf (was in jeder Shell unabhängig vom Verhalten funktioniert).

Ich fange exit ab, weil Shell-Skripte beendet werden können, bevor sie den unteren Rand erreichen - Syntaxfehler, set -e und eine Rückkehr ungleich Null, indem einfach exit aufgerufen wird. Sie können sich nicht darauf verlassen, dass ein Shellscript auf den Grund geht.

SIGQUIT ist Ctrl- \, wenn Sie es noch nie ausprobiert haben. Erhält einen Bonus-Coredump. Ich denke, es lohnt sich auch zu fangen, auch wenn es etwas dunkel ist.

Erfahrungsgemäß haben Sie (wie ich), wenn Sie (wie ich) immer mehrmals Strg-C drücken, manchmal die Hälfte des Bereinigungsteils Ihres Shell-Skripts erreicht. Dies funktioniert also, aber nicht immer so perfekt, wie Sie möchten.

13
ijw