Je cherche un moyen de nettoyer le gâchis lorsque mon script de niveau supérieur se termine.
Surtout si je veux l'utiliser set -e
, je souhaite que le processus d'arrière-plan meure à la fin du script.
Pour nettoyer certains dégâts, trap
peut être utilisé. Il peut fournir une liste de choses exécutées lorsqu'un signal spécifique arrive:
trap "echo hello" SIGINT
mais peut également être utilisé pour exécuter quelque chose si le shell se termine:
trap "killall background" EXIT
C'est une fonction intégrée, help trap
vous donnera donc des informations (fonctionne avec bash). Si vous ne souhaitez supprimer que les jobs d'arrière-plan, vous pouvez le faire
trap 'kill $(jobs -p)' EXIT
Attention à utiliser le single '
, pour éviter que la coque ne se substitue $()
immédiatement.
kill $(jobs -p)
ne fonctionne pas dans le tiret, car il exécute la substitution de commandes dans un sous-shell (voir Substitution de commandes dans le tiret man)killall background
censé être un espace réservé?background
n'est pas dans la page de manuel ...Cela fonctionne pour moi (amélioré grâce aux commentateurs):
kill -- -$$
envoie un SIGTERM à l'ensemble du groupe de processus, tuant ainsi également les descendants.La spécification du signal
EXIT
est utile lors de l'utilisationset -e
(plus de détails ici ).la source
4.3.30(1)-release
OSX, et cela est également confirmé sur Ubuntu . Il y a un wokaround obvoius , cependant :)-$$
. Il évalue à '- <PID> `par exemple-1234
. Dans la page de manuel kill // page de manuel intégrée, un tiret de tête spécifie le signal à envoyer. Cependant - bloque probablement cela, mais le tiret de tête n'est pas documenté autrement. De l'aide?man 2 kill
, ce qui explique que lorsqu'un PID est négatif, le signal est envoyé à tous les processus du groupe de processus avec l'ID fourni ( en.wikipedia.org/wiki/Process_group ). Il est déroutant que cela ne soit pas mentionné dansman 1 kill
ouman bash
, et pourrait être considéré comme un bogue dans la documentation.Mise à jour: https://stackoverflow.com/a/53714583/302079 améliore cela en ajoutant l'état de sortie et une fonction de nettoyage.
Pourquoi convertir
INT
etTERM
quitter? Parce que les deux devraient déclencher lekill 0
sans entrer dans une boucle infinie.Pourquoi déclenchement
kill 0
surEXIT
? Parce que les sorties de script normales devraient se déclencherkill 0
.Pourquoi
kill 0
? Parce que les sous-coquilles imbriquées doivent également être tuées. Cela supprimera tout l'arbre de processus .la source
kill 0
signifie / fait exactement ?Je n'apporterais que des modifications mineures à la réponse de Johannes et j'utiliserais jobs -pr pour limiter le kill aux processus en cours et ajouter quelques signaux supplémentaires à la liste:
la source
La
trap 'kill 0' SIGINT SIGTERM EXIT
solution décrite dans la réponse de @ tokland est vraiment sympa, mais le dernier Bash plante avec une faute de segmentation lors de son utilisation. C'est parce que Bash, à partir de la version 4.3, permet la récursivité des pièges, qui devient infinie dans ce cas:SIGINT
ouSIGTERM
ouEXIT
;kill 0
, qui est envoyéSIGTERM
à tous les processus du groupe, y compris le shell lui-même;Cela peut être contourné en désenregistrant manuellement le piège:
La manière la plus sophistiquée, qui permet d'imprimer le signal reçu et évite les messages "Terminé:":
UPD : ajout d'un exemple minimal;
stop
fonction améliorée pour éviter le piégeage des signaux inutiles et pour masquer les messages "Terminé:" de la sortie. Merci Trevor Boyd Smith pour les suggestions!la source
stop()
vous fournir le premier argument que le numéro de signal , mais alors vous coder en dur les signaux qui sont désinscrits. plutôt que de coder en dur les signaux en cours de désenregistrement, vous pouvez utiliser le premier argument pour vous désenregistrer dans lastop()
fonction (cela pourrait potentiellement arrêter d'autres signaux récursifs (autres que les 3 codés en dur)).SIGINT
, maiskill 0
envoieSIGTERM
, qui sera à nouveau piégé. Cela ne produira cependant pas de récursion infinie, carSIGTERM
il sera piégé lors du deuxièmestop
appel.trap - $1 && kill -s $1 0
devrait probablement fonctionner mieux. Je vais tester et mettre à jour cette réponse. Merci pour la belle idée! :)trap - $1 && kill -s $1 0
ça ne marchera pas aussi, car nous ne pouvons pas tuer avecEXIT
. Mais il suffit vraiment de dé-piégerTERM
, carkill
envoie ce signal par défaut.EXIT
, letrap
gestionnaire de signaux n'est toujours exécuté qu'une seule fois.Pour être sûr, je trouve préférable de définir une fonction de nettoyage et de l'appeler depuis trap:
ou en évitant complètement la fonction:
Pourquoi? Parce qu'en utilisant simplement
trap 'kill $(jobs -pr)' [...]
un, on suppose qu'il y aura des travaux en arrière-plan en cours d'exécution lorsque la condition d'interruption est signalée. Lorsqu'il n'y a pas de tâches, le message suivant (ou similaire) s'affiche:parce que
jobs -pr
c'est vide - je me suis retrouvé dans ce «piège» (jeu de mots voulu).la source
[ -n "$(jobs -pr)" ]
ne fonctionne pas sur mon bash. J'utilise GNU bash, version 4.2.46 (2) -release (x86_64-redhat-linux-gnu). Le message "kill: usage" apparaît toujours.jobs -pr
ne renvoie pas les PID des enfants des processus d'arrière-plan. Il ne détruit pas tout l'arbre du processus, il ne fait que couper les racines.Une belle version qui fonctionne sous Linux, BSD et MacOS X. Essaie d'abord d'envoyer SIGTERM, et si cela ne réussit pas, tue le processus après 10 secondes.
Veuillez noter que les emplois n'incluent pas les processus des petits enfants.
la source
Comme https://stackoverflow.com/a/22644006/10082476 , mais avec un code de sortie ajouté
la source
exit_code
vient leINT TERM
piège?Une autre option consiste à définir le script comme chef de groupe de processus et à intercepter un killpg sur votre groupe de processus à la sortie.
la source
Alors scriptez le chargement du script. Exécutez une commande
killall
(ou tout ce qui est disponible sur votre système d'exploitation) qui s'exécute dès que le script est terminé.la source
jobs -p ne fonctionne pas dans tous les shells s'il est appelé dans un sous-shell, sauf si sa sortie est redirigée dans un fichier mais pas dans un pipe. (Je suppose qu'il était initialement destiné à un usage interactif uniquement.)
Qu'en est-il des éléments suivants:
L'appel à "jobs" est nécessaire avec le shell de tableau de bord de Debian, qui ne met pas à jour le job actuel ("%%") s'il est manquant.
la source
trap 'echo in trap; set -x; trap - TERM EXIT; while kill %% 2>/dev/null; do jobs > /dev/null; done; set +x' INT TERM EXIT; sleep 100 & while true; do printf .; sleep 1; done
Si vous l'exécutez dans Bash (5.0.3) et essayez de terminer, il semble y avoir une boucle infinie. Cependant, si vous le résiliez à nouveau, cela fonctionne. Même par Dash (0.5.10.2-6), vous devez y mettre fin deux fois.J'ai fait une adaptation de la réponse de @ tokland combinée avec les connaissances de http://veithen.github.io/2014/11/16/sigterm-propagation.html quand j'ai remarqué que
trap
cela ne se déclenche pas si j'exécute un processus de premier plan (sans arrière-plan avec&
):Exemple de fonctionnement:
la source
Juste pour la diversité, je publierai une variation de https://stackoverflow.com/a/2173421/102484 , car cette solution conduit au message "Terminé" dans mon environnement:
la source