Bash exécutant plusieurs programmes et manipulant close

J'ai un script bash qui exécute plusieurs programmes

#!/bin/sh python program1.py & python program2.py & other programs ... & lastProgram 

Je l'exécute en tant que ./myscript.sh

Quand je clique sur Ctl-C pour fermer le lastProgram il sort et tous les autres continuent à tourner en arrière-plan. Le problème est que les autres programmes doivent être terminés.

Quelle est la bonne façon de gérer la fermeture de tous les programmes commencés à partir du script?

Collectez les ID de process, tuez les process d'arrière-plan à la sortie.

 #!/bin/bash killbg() { for p in "${pids[@]}" ; do kill "$p"; done } trap killbg EXIT pids=() background job 1 & pids+=($!) background job 2... & pids+=($!) foreground job 

Trapping EXIT exécute la fonction lorsque le shell se ferme, quelle que soit la raison. Vous pouvez changer cela pour trap killbg SIGINT pour l'exécuter uniquement sur ^C

Cela ne vérifie pas si l'un des process d'arrière-plan s'est terminé avant que le script essaie de les tirer. S'ils le font, vous pourriez avoir des erreurs, ou pire, tirer sur le mauvais process.


Ou tuez-les par id de travail. Lisons la sortie des jobs pour savoir lesquels sont encore actifs.

 #!/bin/bash killjobs() { for x in $(jobs | awk -F '[][]' '{print $2}' ) ; do kill %$x done } trap killjobs EXIT sleep 999 & sleep 1 & sleep 999 & sleep 30 

Si vous exécutez des process en arrière-plan qui génèrent d'autres process (comme un sous-shell: (sleep 1234 ; echo foo) & ), vous devez activer le contrôle des tâches avec set -m ("mode moniteur"). Sinon, le process principal est terminé.

Je lisais des questions similaires sur la collecte de PID et ensuite les tuer à la fin d'un script – le problème est qu'un PID pour un process fini pourrait être recykey et réutilisé dans un nouveau process avant que votre script ne se termine, et puis vous pourrait tuer un nouveau process (random) .

Traiter EXIT et tuer avec le contrôle de travail de bash

Vous pouvez utiliser le contrôle de travail de bash pour ne tuer que les process lancés dans le script avec un trap et% n jobspec, en comptant le nombre maximum de jobs qui pourraient être en cours d'exécution (seulement 3 dans cet exemple):

 #!/bin/bash #trap 'kill %1 %2 %3' 0 # 0 or EXIT are equivalent #trap 'kill %1 %2 %3' EXIT # or use {1..n} as below trap 'kill %{1..3}' EXIT sleep 33 & sleep 33 & sleep 33 & echo processes are running, ctrl-c the next sleep sleep 66 echo this line will never be executed 

Tout extra "tue" à des jobspec inexistants qui ont déjà fini seulement résulte dans un message d'erreur, il ne tue aucun autre nouveau / process randoms.


tuer le groupe de process complet de votre script

Voici une façon légèrement différente de tuer le groupe de process complet de votre script. Mais si le contrôle de travail de votre script / shell n'est pas configuré, il pourrait hériter du PPID de son parent … mais sans contrôle de travail, ce qui précède ne fonctionnerait pas non plus.

La différence est cette command kill pour trap, en utilisant le PID de bash, puisqu'elle devient la PGID pour les nouveaux process:

 trap 'kill -- -$$' EXIT 

Regardez ce Q ou ici où le poisson de Johannes Ziemke piège SIGINT et SIGTERM et utilise setsid pour « tuer le groupe de process complet dans un nouveau groupe de process afin que nous ne setsid pas de nous tuer nous-mêmes . )

Si vous souhaitez supprimer tous les process d'arrière-plan s'ils ne sont pas terminés avant la fin de l'exécution de lastProgram , vous devez stocker tous les pids:

 python program1.py & p1pid=$! python program2.py & p2pid=$! other programs ... & p3pid=$! lastProgram kill $p1pid $p2pid $p3pid 

Si vous souhaitez simplement attendre que tous les process en arrière-plan aient fini de s'exécuter avant de quitter le script, vous pouvez utiliser la command wait .

 python program1.py & python program2.py & other programs ... & lastProgram wait