Ce qui se passe est que les deux bash
et ping
reçoivent le SIGINT ( bash
étant pas interactif, à la fois ping
et bash
exécuter dans le même groupe de processus qui a été créé et ensemble comme groupe de processus de premier plan du terminal par le shell interactif vous avez exécuté ce script à partir).
Cependant, bash
gère ce SIGINT de manière asynchrone, uniquement après la fin de la commande en cours d'exécution. bash
sort uniquement à la réception de ce SIGINT si la commande en cours d'exécution meurt d'un SIGINT (c'est-à-dire que son état de sortie indique qu'il a été tué par SIGINT).
$ bash -c 'sh -c "trap exit\ 0 INT; sleep 10; :"; echo here'
^Chere
Au- dessus, bash
, sh
et sleep
recevoir SIGINT quand j'appuyez sur Ctrl-C, mais les sh
sorties normalement avec un code de sortie 0, donc bash
ne tient pas compte du SIGINT, ce qui est la raison pour laquelle nous voyons « ici ».
ping
, du moins celui d’iputils, se comporte comme ça. Lorsqu'il est interrompu, il imprime des statistiques et quitte avec un état de sortie égal à 0 ou 1, selon que ses pings ont été répondus ou non. Ainsi, lorsque vous appuyez sur Ctrl-C en ping
cours d’exécution, bash
vous notez que vous avez appuyé Ctrl-C
dans ses gestionnaires SIGINT, mais que ping
, normalement, il bash
ne se ferme pas.
Si vous ajoutez une sleep 1
boucle dans cette boucle et appuyez sur tant Ctrl-C
que vous êtes en sleep
cours d'exécution, car sleep
il n'y a pas de gestionnaire spécial sur SIGINT, il mourra et signalera bash
qu'il est mort d'un SIGINT et, dans ce cas, il se bash
fermera (il se tuera lui-même avec SIGINT afin signaler l'interruption à son parent).
En ce qui concerne pourquoi bash
se comporte comme ça, je ne suis pas sûr et je remarque que le comportement n'est pas toujours déterministe. Je viens de poser la question sur la bash
liste de diffusion de développement ( Mise à jour : @Jilles a maintenant précisé la raison dans sa réponse ).
Le seul autre shell que j'ai trouvé qui se comporte de manière similaire est ksh93 (Update, comme mentionné par @Jilles, FreeBSD égalementsh
). Là, SIGINT semble être clairement ignoré. Et ksh93
quitte chaque fois qu'une commande est tuée par SIGINT.
Vous obtenez le même comportement que bash
ci-dessus mais aussi:
ksh -c 'sh -c "kill -INT \$\$"; echo test'
Ne pas sortir "test". C'est-à-dire qu'il se ferme (en se tuant avec SIGINT là) si la commande qu'elle attendait meurt de SIGINT, même si elle-même n'a pas reçu ce SIGINT.
Une solution consiste à ajouter un:
trap 'exit 130' INT
En haut du script pour forcer la bash
sortie à la réception d'un SIGINT (notez que dans tous les cas, le SIGINT ne sera pas traité de manière synchrone, uniquement après la fin de la commande en cours d'exécution).
Idéalement, nous aimerions signaler à notre parent que nous sommes morts d'un SIGINT (afin que, s'il s'agit d'un autre bash
script par exemple, ce bash
script soit également interrompu). Faire un exit 130
n'est pas la même chose que mourir de SIGINT (bien que certains obus aient la $?
même valeur dans les deux cas), mais il est souvent utilisé pour signaler un décès par SIGINT (sur les systèmes où SIGINT est 2, ce qui est le plus).
Toutefois , pour bash
, ksh93
ou FreeBSD sh
, cela ne fonctionne pas. Le statut de sortie 130 n'est pas considéré comme une mort par SIGINT et un script parent ne serait pas abandonné là.
Donc, une meilleure alternative serait de se tuer avec SIGINT après avoir reçu SIGINT:
trap '
trap - INT # restore default INT handler
kill -s INT "$$"
' INT
for f in *.txt; do vi "$f"; cp "$f" newdir; done
. Si l'utilisateur tape Ctrl + C lors de la modification de l'un des fichiers,vi
affiche simplement un message. Il semble raisonnable que la boucle se poursuive une fois que l'utilisateur a fini de modifier le fichier. (Et oui, je sais que vous pourriez direvi *.txt; cp *.txt newdir
; je soumets simplement lafor
boucle à titre d'exemple.)