Pourquoi est-ce que `kill -s INT <zsh PID>` se comporte différemment de `Ctrl-C`?

Commençant par:

% donothing () { echo $$; sleep 1000000 } % donothing 47139 

Si à ce moment-là j'appuie sur Ctrl-C au même terminal qui contrôle le shell, alors la fonction donothing s'arrête effectivement, et j'obtiens l'invite de command.

Mais si au contraire, à partir d'une session shell différente, je cours

 % kill -s INT 47139 

… la fonction donothing ne se termine pas . (Ditto si je passe -47139 comme dernier argument à kill .)

Dois-je utiliser un PID différent de celui que j'utilise pour réaliser avec kill -s INT le même effet que je peux réaliser de manière interactive avec Ctrl-C au terminal de contrôle?

(Je suis principalement intéressé par la réponse pour zsh .)


EDIT: en réponse à Gilles commentez: non, je n'ai pas mis de pièges (bien que je serais intéressé d'apprendre à confirmer qu'aucun piège n'est mis en place: y a-t-il un moyen de listr tous les pièges actifs?); et oui, je peux reproduire ce comportement avec zsh -f . En fait, je peux le reproduire de la manière la plus extrême que je connaisse pour get un zsh absolument "nu" (s'il vous plaît laissez-moi savoir s'il y a un meilleur moyen):

 % /usr/bin/env -i /usr/bin/zsh -f % donothing () { echo $$; sleep 1000000 } 12345 

…etc.

Depuis mon post d'origine, j'ai répliqué le comportement décrit ci-dessus à la fois sous Darwin et sous NetBSD, mais pas sous Linux (c'est-à-dire sous Linux kill -s INT <zsh PID> process).

Alors peut-être que c'est une chose BSD-ish. Plus spécifiquement, dans chaque Unix avec lequel j'ai essayé, le process sleep est un enfant du process zsh , mais sous Linux seulement, le signal SIGINT qui a été envoyé au process zsh propagé au process de sleep .

Par conséquent, afin de pouvoir tuer la fonction donothing non interactive sous Darwin et NetBSD, j'ai besoin d'un moyen pour find le PID de l'enfant qui sleep et lui envoyer le SIGINT (ce qui, certes, semble assez cruel) . Plus généralement, pour tuer une fonction shell de manière non interactive sous Darwin et NetBSD, il faut d'abord récursivement / profondeur find les descendants du process zsh et leur envoyer le SIGINT

Control-c du terminal envoie un signal d'interruption au groupe de process associé au terminal, qui inclut le process de sumil. L'envoi de SIGINT via kill n'atteint qu'un seul process, et dans ce cas, le process est incorrect car $$ renvoie l'ID de process du shell, pas le process de sumil. Les shells ignorent normalement SIGINT.

Fondamentalement, vous ne pouvez pas envoyer de signaux à une fonction shell spécifique. Les fonctions sont un outil pratique pour composer des blocs de logique procédurale. Ceci est similaire, mais pas l'équivalent des scripts shell.

Lors de l'exécution d'un script shell, les interpréteurs shell / command line en cours d'exécution forcent le shell / interpréteur enfant à exécuter le script. La fourche implique la création d'un tout nouveau process. Ce process peut être envoyé des signaux arbitraires.

Lors de l'exécution d'une fonction shell, il n'y aura pas de fork (implicite), donc pas de process dédié, donc pas de cible à laquelle un signal pourrait être envoyé.

Lorsque vous appuyez sur Ctrl-C dans le terminal et que le process occupé est le shell lui-même, il arrête juste ce qu'il est en train de faire. Dans votre exemple, il n'est même pas facile de dire si le sleep reçoit le signal (de sorte que la fonction se ferme parce qu'il atteint sa fin) ou si la fonction est arrêtée.

 $ domuch () { for (( i=0; i<1000000; i++)) {} } 

sera de toute façon également stoppé par Ctrl-C , et il n'y a définitivement rien qu'un signal puisse être envoyé.

Si vous voulez envoyer un signal, vous devez impliquer un sous-shell, soit en plaçant vos lignes dans un script, soit en utilisant des backticks (aucune idée comment les afficher ici: – /) ou la notation alternative $()

 $ doless () { $(sleep 1000) } 

Vous pouvez utiliser le $! paramètre spécial qui vous indique le PID du process d'arrière-plan le plus récemment démarré, mais seulement dans le même shell, alors vous pouvez aller:

 $ doless & $ kill -int $! [1] 30769 [1] + interrupt dosome 

Ce n'est pas la fonction qui reçoit le signal, mais le sleep .

Fait intéressant, si vous dissortingbuez le sumil à l'arrière-plan de la fonction comme dans

 dotoomuch() () { $(sleep 1000) & } 

vous ne serez pas en mesure de l'interrompre par des moyens si Ctrl-C , bien qu'il bloque le terminal. Aucune idée si c'est le comportement désiré (zsh 4.3.10).