web-dev-qa-db-fra.com

"piège ... INT TERM EXIT" vraiment nécessaire?

De nombreux exemples de trap utilisent trap ... INT TERM EXIT pour les tâches de nettoyage. Mais est-il vraiment nécessaire d'énumérer les trois sigspecs?

Le manuel dit:

Si SIGNAL_SPEC est EXIT (0) ARG est exécuté à la sortie du Shell.

ce qui, je crois, s'applique que le script se termine normalement ou qu'il se termine parce qu'il a reçu SIGINT ou SIGTERM. Une expérience confirme également ma conviction:

$ 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

Alors pourquoi tant d'exemples répertorient tous les INT TERM EXIT? Ou ai-je raté quelque chose et y a-t-il un cas où une semelle EXIT manquerait?

71
musiphil

Le spécification POSIX ne dit pas grand-chose sur les conditions résultant de l'exécution du trap EXIT, seulement sur ce à quoi son environnement doit ressembler lors de son exécution.

Dans le shell ash de Busybox, votre test de sortie de piège ne fait pas écho à "TRAP" avant de quitter en raison de SIGINT ou SIGTERM. Je soupçonne qu'il existe d'autres obus qui ne fonctionnent pas aussi bien de cette façon.

# /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

Oui, il y a une différence.

Ce script se ferme lorsque vous appuyez sur Enter, ou envoyez-le SIGINT ou SIGTERM:

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

Ce script se ferme lorsque vous appuyez sur Enter:

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

* Testé dans sh , Bash , et Zsh . (ne fonctionne plus dans sh lorsque vous ajoutez une commande pour que trap s'exécute)


Il y a aussi ce que @Shawn a dit: Ash et Dash ne piège pas signaux avec EXIT.

Donc, pour gérer les signaux de manière robuste, il est préférable d'éviter complètement de piéger EXIT et d'utiliser quelque chose comme ceci:

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

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

Affiner la dernière réponse, car elle a des problèmes:

# 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

Points ci-dessus:

Les gestionnaires INT et TERM ne se quittent pas pour moi lorsque je teste - ils gèrent l'erreur puis le Shell revient à la sortie (et ce n'est pas trop surprenant). Je m'assure donc que le nettoyage se termine après, et dans le cas des signaux utilise toujours un code d'erreur (et dans l'autre cas d'une sortie normale, conserve le code d'erreur).

Avec bash, il semble que quitter dans le gestionnaire INT appelle également le gestionnaire EXIT, donc je décompresse le gestionnaire de sortie et l'appelle moi-même (qui fonctionnera dans n'importe quel shell quel que soit le comportement).

Je piège exit parce que les scripts Shell peuvent quitter avant qu'ils n'atteignent le bas - erreurs de syntaxe, set -e et un retour différent de zéro, en appelant simplement exit. Vous ne pouvez pas compter sur un shellscript pour aller au fond.

SIGQUIT est Ctrl-\si vous ne l'avez jamais essayé. Vous obtient un bonus coredump. Je pense donc que cela vaut également la peine d'être piégé, même si c'est un peu obscur.

L'expérience montre que si vous (comme moi) appuyez toujours plusieurs fois sur Ctrl-C, vous l'attraperez parfois à mi-chemin dans la partie nettoyage de votre script Shell, donc cela fonctionne mais pas toujours aussi parfaitement que vous le souhaitez.

13
ijw