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) .
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.
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