例にはいくつかの問題があります。最後に、問題の解決策があります。
最初のスクリプトはwait
ステートメントを欠いているように見えるため、約 3 秒後に終了します。ただしscript2
、メモリに残り、実行されます。
SIGINT シグナルを送信するプロセスを bash に自動的に判断させるにはどうすればよいですか?
実際bash
にはバックグラウンド プロセスで無効化SIGINT
(およびSIGQUIT
) され、有効化することはできません (trap
コマンドを単独で実行して、設定されたトラップの現在のステータスを確認できます)。スクリプトからスクリプトにシグナル SIGINT を送信する方法を参照してください。バッシュ
バックグラウンド プロセスであるためscript2
、トラップを設定していません。両方とも無視され、バックグラウンド プロセスでトラップまたはリセットすることはできません。SIGINT
SIGINT
SIGQUIT
参考として、問題に関連する bash のドキュメントを次に示します。
バックグラウンド プロセスに対するプロセス グループ ID の影響 (ドキュメントのジョブ制御セクション):
[...] プロセス グループ ID が現在のターミナル プロセス グループ ID と等しいプロセスは、[..] SIGINT などのキーボード生成シグナルを受け取ります。これらのプロセスは、フォアグラウンドにあると言われています。
バックグラウンド プロセスは、プロセス グループ ID が端末のものとは異なるプロセスです。このようなプロセスは、キーボードで生成された信号の影響を受けません。
SIGINT
andのデフォルト ハンドラーSIGQUIT
(ドキュメントのシグナルセクション):
bash によって実行される非組み込みコマンドには、シェルが親から継承した値に設定されたシグナル ハンドラーがあります。ジョブ制御が有効でない場合、非同期コマンドは、これらの継承されたハンドラーに加えて、SIGINT と SIGQUIT を無視します。
およびトラップの変更について(trap
組み込みドキュメント内):
シェルに入ったときに無視されたシグナルは、トラップまたはリセットできません。
解決策 1
次のように変更script1
します。
#!/bin/bash
{ ./script2; } &
sleep 1
subshell_pid=$!
pid=$(ps -ax -o ppid,pid --no-headers | sed -r 's/^ +//g;s/ +/ /g' |
grep "^$subshell_pid " | cut -f 2 -d " ")
kill -SIGINT $pid
sleep 2
wait ## Don't forget this.
これはどのように作動しますか ?実際には、と を使用する{
とサブシェルが作成されますが、このサブシェルはバックグラウンド プロセスであるため、 で}
説明した制限によって制限されます。SIGINT
ただし、サブシェル自体のサブプロセスはフォアグラウンドであり、バックグラウンド プロセスではありません (サブシェル スコープの場合)... 結果として、トラップまたはリセットSIGINT
してSIGQUIT
シグナルを送信できます。
秘訣は、サブシェルでこのサブプロセスのpidを見つけることです。ここではps
、サブシェルのpidを親pidとして持つ唯一のプロセスを見つけるために使用します。
解決策 2
実際には、ジョブとして管理される直接の新しいプロセスのみが SIGINT と SIGQUIT を無視します。単純な bash 関数はそうではありません。したがって、script2
コードが をソースとする関数内にある場合、これは他に何も必要としないscript1
new になります。script1
#!/bin/bash
script2() {
## script2 code
echo "~~ENTRY"
trap 'echo you hit ctrl-c, waking up...' SIGINT
sleep infinity
echo "~~EXIT"
}
## script1 code
script2 &
sleep 1
kill -SIGINT $!
sleep 2
これも機能します。舞台裏では、ソリューション 1 と同じメカニズムが機能しています。bash 関数は構造に非常に近い{
}
です。