web-dev-qa-db-fra.com

Exécutez plusieurs commandes et tuez-les comme une seule dans bash

Je veux exécuter plusieurs commandes (processus) sur un seul shell. Tous ont leur propre sortie continue et ne s'arrêtent pas. Les exécuter en arrière-plan se casse Ctrl-C. Je voudrais les exécuter comme un seul processus (sous-shell, peut-être?) Pour pouvoir tous les arrêter avec Ctrl-C.

Pour être précis, je veux exécuter des tests unitaires avec mocha (mode de surveillance), exécuter le serveur et exécuter un prétraitement de fichier (mode de surveillance) et voir la sortie de chacun dans une fenêtre de terminal. Fondamentalement, je veux éviter d'utiliser un exécuteur de tâches.

Je peux le réaliser en exécutant des processus en arrière-plan (&), mais je dois les mettre au premier plan pour les arrêter. Je voudrais avoir un processus pour les envelopper et quand j'arrête le processus, il arrête ses "enfants".

55
user1876909

Pour exécuter des commandes simultanément, vous pouvez utiliser le & séparateur de commandes.

~$ command1 & command2 & command3

Cela commencera command1, puis l'exécute en arrière-plan. De même command2. Puis ça commence command3 normalement.

La sortie de toutes les commandes sera brouillée ensemble, mais si ce n'est pas un problème pour vous, ce serait la solution.

Si vous souhaitez examiner la sortie ultérieurement, vous pouvez diriger la sortie de chaque commande dans tee, ce qui vous permet de spécifier un fichier pour refléter la sortie.

~$ command1 | tee 1.log & command2 | tee 2.log & command3 | tee 3.log

La sortie sera probablement très désordonnée. Pour contrer cela, vous pouvez donner à la sortie de chaque commande un préfixe en utilisant sed.

~$ echo 'Output of command 1' | sed -e 's/^/[Command1] /' 
[Command1] Output of command 1

Donc, si nous mettons tout cela ensemble, nous obtenons:

~$ command1 | tee 1.log | sed -e 's/^/[Command1] /' & command2 | tee 2.log | sed -e 's/^/[Command2] /' & command3 | tee 3.log | sed -e 's/^/[Command3] /'
[Command1] Starting command1
[Command2] Starting command2
[Command1] Finished
[Command3] Starting command3

Il s'agit d'une version hautement idéalisée de ce que vous allez probablement voir. Mais c'est le meilleur auquel je puisse penser en ce moment.

Si vous souhaitez les arrêter tous en même temps, vous pouvez utiliser la génération dans trap.

~$ trap 'kill %1; kill %2' SIGINT
~$ command1 & command2 & command3

Cela exécutera command1 et command2 en arrière-plan et command3 au premier plan, ce qui vous permet de le tuer avec Ctrl+C.

Lorsque vous tuez le dernier processus avec Ctrl+C le kill %1; kill %2 les commandes sont exécutées, car nous avons lié leur exécution à la réception d'un INTerupt SIGnal, la chose envoyée en appuyant sur Ctrl+C.

Ils tuent respectivement les 1er et 2e processus d'arrière-plan (votre command1 et command2). N'oubliez pas de supprimer le piège, une fois vos commandes terminées à l'aide de trap - SIGINT.

Monstre complet d'une commande:

~$ trap 'kill %1; kill %2' SIGINT
~$ command1 | tee 1.log | sed -e 's/^/[Command1] /' & command2 | tee 2.log | sed -e 's/^/[Command2] /' & command3 | tee 3.log | sed -e 's/^/[Command3] /'

Vous pouvez bien sûr jeter un œil à écran . Il vous permet de diviser votre console en autant de consoles séparées que vous le souhaitez. Vous pouvez donc surveiller toutes les commandes séparément, mais en même temps.

70
Minix

Vous pouvez facilement tuer un tas de processus à la fois si vous vous arrangez pour les exécuter (et seulement eux) dans le même groupe de processus .

Linux fournit l'utilitaire setsid pour exécuter un programme dans un nouveau groupe de processus (même dans une nouvelle session, mais cela nous importe peu). (C'est faisable mais plus compliqué sans setsid.)

L'ID de groupe de processus (PGID) d'un groupe de processus est l'ID de processus du processus parent d'origine dans le groupe. Pour tuer tous les processus d'un groupe de processus, passez le négatif du PGID à l'appel ou à la commande système kill. Le PGID reste valide même si le processus d'origine avec ce PID meurt (bien que cela puisse être un peu déroutant).

setsid sh -c 'command1 & command2 & command3' &
pgid=$!
echo "Background tasks are running in process group $pgid, kill with kill -TERM -$pgid"

Si vous exécutez les processus en arrière-plan à partir d'un shell non interactif , ils resteront tous dans le groupe de processus du shell. Ce n'est que dans des shells interactifs que les processus d'arrière-plan s'exécutent dans leur propre groupe de processus. Par conséquent, si vous créez des commandes à partir d'un shell non interactif qui reste au premier plan, Ctrl+C va tous les tuer. Utilisez la fonction intégrée wait pour faire en sorte que le shell attende que toutes les commandes se terminent.

sh -c 'command1 & command2 & command3 & wait'
# Press Ctrl+C to kill them all

Je suis surpris que ce ne soit pas encore là, mais ma façon préférée de le faire est d'utiliser sous-coquilles avec les commandes d'arrière-plan. Usage:

(command1 & command2 & command3)

La sortie est visible des deux, toutes les commandes et commodités bash sont toujours disponibles, et un seul Ctrl-c les tue tous. Je l'utilise généralement pour démarrer un serveur de fichiers client de débogage en direct et un serveur principal en même temps, afin de pouvoir les tuer facilement quand je le souhaite.

La façon dont cela fonctionne est que command1 et command2 s'exécutent en arrière-plan d'une nouvelle instance de sous-shell et command3 est au premier plan de cette instance, et le sous-shell est ce qui reçoit le premier le signal de mise à mort d'un Ctrl-c appuyez sur la touche. Cela ressemble beaucoup à l'exécution d'un script bash en ce qui concerne qui possède quel processus et quand les programmes d'arrière-plan sont tués.

17
jv-dev

Vous pouvez utiliser le point-virgule ; ou && comme ça:

cmd1; cmd2   # Runs both the commands, even if the first one exits with a non-zero status.

cmd1 && cmd2 # Only runs the second command if the first one was successful.
0
serenesat