なぜ `kill -s INT`は`Ctrl-C`とは異なる動作をしますか?

なぜ `kill -s INT`は`Ctrl-C`とは異なる動作をしますか?

次から始めましょう:

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

この時点でシェルを制御する同じ端末をクリックすると、関数が終了してCtrl-Cコマンドdonothingプロンプトに戻ります。

しかし、代わりに別のシェルセッションで次を実行すると、

% kill -s INT 47139

...このdonothing機能はいいえ終了。 (-47139最後の引数として渡すと上記と同じですkill。)

kill -s INT制御端末で対話的に同じ効果を得るには、使用しているPIDとは異なるPIDを使用する必要がありますか?Ctrl-C

(私は主に答えに興味がありますzsh。)


編集:Gillesのコメントへの回答:いいえ、トラップを設定していません(トラップが設定されていないことを確認する方法を学びたい。すべてのアクティブトラップを一覧表示する方法はありますか?はい、再現できます)。この動作はzsh -f。実際、完全に「裸」の効果を得るために、私が知っている最も極端な方法でそれを再現することができます。zsh(より良い方法があれば教えてください。)

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

...など。

元の投稿以降、DarwinとNetBSDは上記の動作を複製しましたが、Linuxでは複製しませんでした(つまり、Linuxでは上記で定義した関数を終了しますが、kill -s INT <zsh PID>プロセスdonothingの親シェルは終了しません)。

おそらくこれはBSDスタイルです。具体的には、私が試したすべてのUnixでは、プロセスはプロセスのsleep子ですが、zshLinuxでのみプロセスSIGINTに送信された信号がプロセスに伝播されます。zshsleep

したがって、DarwinとNetBSDでこの関数を非対話式で終了させるには、donothing-ingサブプロセスのPIDを見つけてそのプロセスにsleep送信する方法が必要ですSIGINT(もちろん、これは本当に冷静に聞こえます)。より一般的には、DarwinとNetBSDで非対話式でシェル機能を終了するには、再帰的/深く優先検索を実行する必要があります。子孫処理zshして送ってくださいSIGINT...

答え1

Control-cスリープ処理を含む端末から、端末に関連するプロセスグループに割り込み信号を送信します。 killを介してSIGINTを送信すると、1つのプロセスにのみ到達できます。この場合、$ $は休止プロセスではなくシェルのプロセスIDを返すため、無効なプロセスになります。シェルは通常 SIGINT を無視します。

答え2

デフォルトでは、次のことができます。いいえ特定のシェル関数にシグナルを送信します。関数は手続き型論理ブロックを構築するのに便利なツールです。これはシェルスクリプトと似ていますが、同じではありません。

シェルスクリプトを実行すると、現在実行中のシェル/インタプリタが分岐し、サブシェル/インタプリタがスクリプトを実行できるようになります。 Forking はまったく新しいプロセスを作ることを意味します。プロセスは任意の信号を送信することができます。

シェル機能を実行すると(暗黙的)分岐がないため、専用のプロセスがないため、シグナルを送信できるターゲットはありません。

ターミナルを押すと、Ctrl-C忙しいプロセスはシェル自体であり、現在実行中のすべての操作を停止します。あなたの例では、sleep信号が受信されたか(関数が最後に到達して終了するように)、または関数の実行が停止したかどうかを話すことは困難です。

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

とにかくブロックされ、Ctrl-C送信する信号がまったくありません。

シグナルを送信するには、スクリプトに行を入れるか、バックティック(ここで表示する方法がわかりません:-/)または代替記号を使用してサブシェルを含める必要があります$()

$ doless () { $(sleep 1000) }

$!最後に開始されたバックグラウンドプロセスのPIDを示す特別な引数を使用できますが、同じシェル内でのみ可能で、次のことができます。

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

まだ信号を受信する機能ではありませんsleep

興味深いことに、下のような関数の中から背景にスリープを渡すと

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

Ctrl-Cifで端末を遮断しても中断することはできません。これが望ましい動作であるかどうかはわかりません(zsh 4.3.10)。

関連情報