Ce qui se passe est que les deux bashet pingreçoivent le SIGINT ( bashétant pas interactif, à la fois pinget bashexé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, bashgère ce SIGINT de manière asynchrone, uniquement après la fin de la commande en cours d'exécution. bashsort 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, shet sleeprecevoir SIGINT quand j'appuyez sur Ctrl-C, mais les shsorties normalement avec un code de sortie 0, donc bashne 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 pingcours d’exécution, bashvous notez que vous avez appuyé Ctrl-Cdans ses gestionnaires SIGINT, mais que ping, normalement, il bashne se ferme pas.
Si vous ajoutez une sleep 1boucle dans cette boucle et appuyez sur tant Ctrl-Cque vous êtes en sleepcours d'exécution, car sleepil n'y a pas de gestionnaire spécial sur SIGINT, il mourra et signalera bashqu'il est mort d'un SIGINT et, dans ce cas, il se bashfermera (il se tuera lui-même avec SIGINT afin signaler l'interruption à son parent).
En ce qui concerne pourquoi bashse 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 bashliste 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 ksh93quitte chaque fois qu'une commande est tuée par SIGINT.
Vous obtenez le même comportement que bashci-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 bashsortie à 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 bashscript par exemple, ce bashscript soit également interrompu). Faire un exit 130n'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, ksh93ou 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,viaffiche 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 laforboucle à titre d'exemple.)