De nombreux exemples à trap
utiliser 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.
que 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 énumèrent-ils tous INT TERM EXIT
? Ou ai-je oublié quelque chose et y a-t-il un cas où une semelle EXIT
manquerait?
INT TERM EXIT
le code de nettoyage est exécuté deux fois quandSIGTERM
ouSIGINT
est reçu.Réponses:
La spécification POSIX ne dit pas grand-chose sur les conditions qui entraînent l'exécution de l'interruption EXIT, mais uniquement sur l'apparence de son environnement lors de son exécution.
Dans le shell de cendres de Busybox, votre test de sortie de trappe ne fait pas écho à "TRAP" avant de quitter en raison de SIGINT ou de SIGTERM. Je soupçonne qu’il existe d’autres coquilles qui pourraient ne pas fonctionner de la même manière.
la source
dash
aussi ne pas piéger juste au momentEXIT
où il reçoitSIGINT/SIGTERM
.zsh
ainsi - peutbash
- être est-il le seul shell où correspondEXIT
également les signaux.zsh
pasEXIT
quand il reçoitINT
, mais quand il reçoitTERM
. EDIT: Je viens de remarquer quel âge c'était ...Oui, il y a une différence.
Ce script se fermera lorsque vous appuierez sur Enter, ou l'enverrez
SIGINT
ouSIGTERM
:Ce script se ferme lorsque vous appuyez sur Enter:
* 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ègent pas les signaux avec
EXIT
.Donc, pour gérer les signaux de manière robuste, il est préférable d’éviter
EXIT
tout interception et d’utiliser quelque chose comme ceci:la source
mktemp
appels.exit
nécessaire danscleanup
?ERR
, mais ce n'est pas portable .trap - INT TERM; kill -2 $$
tant que dernière ligne de nettoyage, vous souhaiterez dire au shell parent qu’il s’est arrêté prématurément. Si un shell parent foobar.sh appelle votre script (foo.sh), puis bar.sh, vous ne voulez pas que bar.sh soit exécuté si INT / TERM est envoyé à votre foo.sh.trap cleanup EXIT
gérera automatiquement cette propagation, ce qui en fait l’IMO la plus robuste. Cela signifie également que vous n’auriez pas à appelercleanup
à la fin du script.Affiner la dernière réponse, car elle a des problèmes:
Points ci-dessus:
Les gestionnaires INT et TERM ne s'arrêtent pas pour moi lorsque je teste - ils gèrent l'erreur, puis le shell retourne à la sortie (et ce n'est pas trop surprenant). Je m'assure donc que le nettoyage se termine par la suite et, dans le cas des signaux, utilise toujours un code d'erreur (et dans l'autre cas, une sortie normale conserve le code d'erreur).
Avec bash, il semble que quitter dans le gestionnaire INT appelle également le gestionnaire EXIT. C'est pourquoi je détache le gestionnaire de sortie et l'appelle moi-même (ce qui fonctionnera dans n'importe quel shell, quel que soit le comportement).
J'interromps l'exit parce que les scripts de shell peuvent se terminer avant qu'ils atteignent le bas - erreurs de syntaxe, set -e et un retour différent de zéro, appelant simplement l'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. Donc, je pense que cela vaut également la peine d'être piégé, même si c'est un peu obscur.
L’expérience passée montre que si vous (comme moi) appuyez toujours sur Ctrl-C à plusieurs reprises, vous l’accepterez parfois à mi-chemin de la partie nettoyage de votre script shell, donc cela fonctionne mais pas toujours aussi parfaitement que vous le souhaitez.
la source
trap
l'appelant obtiendrait 130 pour SIGINT, 143 pour SIGTERM, etc. Donc , je capturer et transmettre le code de sortie correcte comme:sig_cleanup() { err=$?; trap '' EXIT; (exit $err); cleanup; }
.trap '' EXIT INT TERM
la fonction de nettoyage? Est-ce pour empêcher l'utilisateur d'interrompre accidentellement le nettoyage que vous avez mentionné dans le dernier paragraphe? LeEXIT
redondant n'est-il pas ?