Ctrl-C avec deux commandes simultanées en bash

15

Je veux exécuter deux commandes simultanément en bash sur une machine Linux. Par conséquent, dans mon ./execute.shscript bash, je mets:

command 1 & command 2
echo "done"

Cependant, lorsque je veux arrêter le script bash et appuyer sur Ctrl+ C, seule la deuxième commande est arrêtée. La première commande continue de s'exécuter. Comment m'assurer que le script bash complet est arrêté? Ou dans tous les cas, comment arrêter les deux commandes? Parce que dans ce cas, peu importe la fréquence à laquelle j'appuie sur Ctrl+, Cla commande continue de fonctionner et je suis obligé de fermer le terminal.

maero21
la source
Transformez la question de suivi en une question distincte et supprimez-la ici. Maintenant, il n'est pas clair pour un visiteur si les deux questions ont été répondues, ou juste la question initiale.
Zelda

Réponses:

17

Si vous tapez

command 1 & command 2

c'est égal à

command 1 &
command 2

c'est à dire que cela exécutera la première commande en arrière-plan, puis exécutera la deuxième commande en premier plan. Cela signifie en particulier que votre echo "done"est imprimé une fois command 2terminé, même s'il command 1est toujours en cours d'exécution.

Tu veux probablement

command 1 &
command 2 &
wait
echo "done"

Cela exécutera les deux commandes en arrière-plan et attendra que les deux se terminent.


Si vous appuyez sur CTRL-C, cela n'enverra que le signal SIGINT au processus de premier plan, c'est- command 2à- dire dans votre version ou waitdans ma version.

Je suggère de mettre un piège comme ça:

#!/bin/bash

trap killgroup SIGINT

killgroup(){
  echo killing...
  kill 0
}

loop(){
  echo $1
  sleep $1
  loop $1
}

loop 1 &
loop 2 &
wait

Avec le piège, le signal SIGINT produit par CTRL-C est piégé et remplacé par la killgroupfonction, qui tue tous ces processus.

michas
la source
Merci pour votre réponse! J'ai cependant une question complémentaire. J'ai maintenant le script suivant: trap killgroup SIGINT killgroup () {echo kill ... kill 0} command001 command1 & command2 J'ai omis la commande d'attente car le script est autorisé à continuer lorsque command2 est terminé. Cependant, il semble que la commande 1 démarre déjà lorsque j'exécute le script. Puis-je résoudre ce problème? Merci
maero21
command1 démarre directement une fois la commande001 terminée. vous pouvez utiliser set -xau début de votre script pour imprimer les commandes qui sont exécutées.
michas
6

Lorsque vous placez une commande en arrière-plan à partir d'un script, le PID ne s'affiche pas à l'écran. Vous pouvez utiliser la variable intégrée $!qui stocke le PID du dernier processus afin que vous puissiez capturer le PID de command1.

command1 &
echo $!

ferait écho au PID de command1.

Bash fournit également le piège intégré que vous pouvez utiliser pour enregistrer une séquence de commandes à exécuter lorsque des signaux particuliers sont reçus. Vous pouvez l'utiliser pour attraper la commande SIGINT et kill1 avant de quitter le script principal, par exemple

#!/bin/bash

onINT() {
echo "Killing command1 $command1PID too"
kill -INT "$command1PID"
exit
}

trap "onINT" SIGINT
command1 &
command1PID="$!"
comamnd2
echo Done

Maintenant, pendant que la commande 2 est en cours d'exécution, la frappe Ctrl Centraînera l'envoi à la fois de la commande1 et de la commande2 SIGINT.

Communauté
la source
Vous pouvez également utiliser la %nsyntaxe pour supprimer des travaux d'arrière-plan spécifiques dans ce shell. Habituellement, vous émettez kill %1pour tuer le dernier et le seul processus d'arrière-plan. Avec plus de processus d'arrière-plan, utilisez jobspour voir la liste en premier.
9000
@ 9000: Oui, mais l'OP exécute ses commandes dans un script (./execute.sh), donc le travail est-il beaucoup plus problématique?
@lain: certainement. J'ai manqué le point sur l'exécution d'un script.
9000
5

Les nouvelles versions de GNU Parallel feront ce que vous voulez:

parallel ::: "command 1" "command 2"
echo "done"

Ou si commandreste le même:

parallel command ::: 1 2
echo done

Regardez la vidéo d'introduction pour une introduction rapide: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

Parcourez le didacticiel (man parallel_tutorial). Vous commandez en ligne avec amour pour vous.

Ole Tange
la source
1

Ctrl+ Cenvoie un signal SIGINT à votre processus frontal, ce qui est command2. command1est exécuté en arrière-plan, donc pas concerné par le flux d'entrée.

Lorsque vous tapez command1 &, bash devrait vous donner le PID du processus, quelque chose comme [1234]. Pour tuer ce processus, vous pouvez utiliser kill 1234. Maintenant, si vous avez les PID des deux command1et command2(jetez un œil à ps -ef), vous pouvez les utiliser killpour les terminer tous:

kill pid1 pid2 pid3 ...

Une petite astuce serait d'exécuter les deux commandes en arrière-plan, avec:

command1 & command2 &

Bash vous donnera les deux PID, prêts à être tués. Une autre astuce serait de ramener command1au premier plan une fois que vous avez tué command2:

command1 & command2
# Hit Ctrl+C : command2 terminates.

fg # Bring back command1 to foreground.
# Hit Ctrl+C again, command1 terminates.

Plus d'informations disponibles ici: http://linuxg.net/how-to-manage-background-and-foreground-processes/

John WH Smith
la source