INTのトラップはサブシェルでは機能しません。

INTのトラップはサブシェルでは機能しません。
$ bash -c "trap \"echo INT\" INT; sleep 3" & pid=$!; sleep 1; kill -INT $pid; wait
[1] 27811
INT
[1]+  Done                    bash -c "trap \"echo INT\" INT; sleep 3"

$ (bash -c "trap \"echo INT\" INT; sleep 3" & pid=$!; sleep 1; kill -INT $pid; wait)

SIGINT2番目の場合にハンドラが呼び出されない理由を説明できますか?

答え1

ジョブ制御は、ユーザーが単一のログインセッション内で複数のプロセスグループ(またはジョブ)間を移動できるようにするプロトコルを表します。

https://www.gnu.org/software/libc/manual/html_node/Job-Control.html

通常、対話型シェルでは有効になり、非対話型シェルでは無効になります。

$ echo $-; sleep 1 & fg
himBHs
[1] 84366
sleep 1

$ bash -c 'echo $-; sleep 1 & fg'
hBc
bash: line 1: fg: no job control

この場合...明らかにジョブ制御が無効になっているため、$-信頼できません。

$ (echo $-; sleep 1 & fg)
himBHs
bash: fg: no job control

シェルはジョブを各パイプラインに関連付けます。

https://www.gnu.org/software/bash/manual/html_node/Job-Control-Basics.html

つまり、ジョブ制御が有効になると、各パイプラインは別々のプロセスグループで実行されます。

pgid.sh:

#!/usr/bin/env bash
ps -o pgid= $$
$ ./pgid.sh >&2 | ./pgid.sh >&2; ./pgid.sh; ./pgid.sh & wait
  93439
  93439
  93443
[1] 93445
  93445
[1]+  Done                    ./a.sh

$ (./pgid.sh >&2 | ./pgid.sh >&2; ./pgid.sh; ./pgid.sh & wait)
  93749
  93749
  93749
  93749

タスクの1つはフォアグラウンドタスクで、残りはバックグラウンドタスクです。

バックグラウンドタスクがありますしてはいけないそれらを始めた殻に縛られています。シェルを終了すると、引き続き実行されます。したがって、SIGINT基本的に中断してはいけません。ジョブ制御が有効になると、バックグラウンドジョブは別のプロセスグループで実行されるため、このジョブは自動的に実行されます。ジョブ制御が無効になると、非同期bashコマンドは無視され、そのSIGINTコマンド(スクリプトの場合bash)はそれを上書きすることはできません。

つまり、ここでは次のようになります。

$ bash -c "trap 'echo INT' INT; sleep 3" & pid=$!; sleep 1; kill -INT "$pid"; wait

バックグラウンドジョブ(bash -c "trap 'echo INT' INT; sleep 3")は、ジョブ制御が有効になっている対話型シェルによって実行されます。結果バックグラウンドジョブが受信されましたSIGINT

これをジョブ制御なしで非対話型シェルでラップすると、次のようになります。

$ (bash -c "trap 'echo INT' INT; sleep 3" & pid=$!; sleep 1; kill -INT "$pid"; wait)

bash -c "trap 'echo INT' INT; sleep 3"無視されSIGINTtrap ... INTまた無視されます。

これは次のように確認できます。

$ bash -c "trap 'echo INT' INT; trap; sleep 3" & pid=$!; sleep 1; kill -INT "$pid"; wait
[1] 293631
trap -- 'echo INT' SIGINT
trap -- '' SIGFPE
INT
[1]+  Done                    bash -c "trap 'echo INT' INT; trap; sleep 3"

$ (bash -c "trap 'echo INT' INT; trap; sleep 3" & pid=$!; sleep 1; kill -INT "$pid"; wait)
trap -- '' SIGINT
trap -- '' SIGQUIT
trap -- '' SIGFPE

$ bash -c 'ps -o pid,ignored,comm,args -p $$' & wait
[1] 345833
    PID          IGNORED COMMAND         COMMAND
 345833 0000000000000000 ps              ps -o pid,ignored,comm,args -p 345833
[1]+  Done                    bash -c 'ps -o pid,ignored,comm,args -p $$'

$ (bash -c 'ps -o pid,ignored,comm,args -p $$' & wait)
    PID          IGNORED COMMAND         COMMAND
 345629 0000000000000006 ps              ps -o pid,ignored,comm,args -p 345629

関連する引用:

Bashによって実行される非組み込みコマンドは、シグナルハンドラをシェルが親から継承した値に設定します。ジョブ制御が無効になると、非同期コマンドは無視されSIGINTます。SIGQUITこれらの継承されたハンドラに加えて、コマンドの置き換えの結果として実行されるコマンドは、キーボードから生成されたジョブ制御信号とを無視しますSIGTTINSIGTTOUSIGTSTP

https://www.gnu.org/software/bash/manual/html_node/Signals.html

シェルに入ると無視される信号はキャプチャまたはリセットできません。

https://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Builtins.html#index-trap

ジョブ制御とは、プロセスの実行を選択的に停止(一時停止)し、後で実行を再開(再開)する機能を指します。ユーザーは通常、オペレーティングシステムカーネルのターミナルドライバとBashが提供するインタラクティブインタフェースを介してこの機能を使用します。

シェルはジョブを各パイプラインに関連付けます。これjobsには、コマンドを使用して一覧表示できる現在実行中の作業テーブルがあります。 Bashがジョブを非同期的に開始すると、次の行が印刷されます。

[1] 25647

このジョブがジョブ番号であり、1このジョブに関連付けられているパイプラインの最後のプロセスのプロセスIDがあることを示します25647。単一パイプライン内のすべてのプロセスは同じタスクのメンバーです。 Bash は、ジョブ抽象化をジョブ制御の基礎として使用します。

タスク制御ユーザーインターフェースの実装を容易にするために、オペレーティングシステムは現在の端末プロセスグループIDの概念を維持しています。このプロセスグループのメンバー(現在の端末のプロセスグループIDと同じプロセスグループIDを持つプロセス)は、キーボードから生成された信号を受け取りますSIGINT。これらのプロセスは前景にあると言われています。バックグラウンドプロセスは、プロセスグループIDが端末プロセスグループIDと異なるプロセスである。これらのプロセスはキーボードから生成された信号の影響を受けません。フォアグラウンドプロセスのみが端末からデータを読み取ることができ、ユーザーが指定した場合はstty tostop端末に書き込むことができます。端末から読み取り(stty tostopまたは有効な場合は書き込み)を試みるバックグラウンドプロセスは、カーネルの端末ドライバから送信された()信号を持ち、捕捉されない限り停止しSIGTTINます。SIGTTOU

https://www.gnu.org/software/bash/manual/html_node/Job-Control-Basics.html

関連情報