SIGINTについて2つのテストを行いました Ctrl+C bashで。
親スクリプトと子スクリプトの両方がtrap
によってハンドラーを設定します。
# !/bin/bash
# Parent.sh
trap 'echo "I handled the SIGINT in Parent.sh and expect the script going ahead."' SIGINT
echo 1.1
./Child.sh
echo 1.2
# !/bin/bash
# Child.sh
trap 'echo "I handled the SIGINT in Child.sh and expect the script going ahead."' SIGINT
echo 2.1
sleep 1000000000
echo 2.2
/tmp:$ ./Parent.sh
1.1
2.1
^CI handled the SIGINT in Child.sh and expect the script going ahead.
2.2
I handled the SIGINT in Parent.sh and expect the script going ahead.
1.2
出力は私の期待とかなり一致しています。
子スクリプトのみがtrap
によってハンドラーを設定します。
# !/bin/bash
# Parent.sh
# trap 'echo "I expect I will be terminated immediately after Child.sh died."' SIGINT
echo 1.1
./Child.sh
echo "sleep started"
sleep 5
echo "sleep finished"
echo 1.2
# !/bin/bash
# Child.sh
trap 'echo "I handled the SIGINT in Child.sh and expect the script going ahead."' SIGINT
echo 2.1
sleep 1000000000
echo 2.2
/tmp:$ ./Parent.sh
1.1
2.1
^CI handled the SIGINT in Child.sh and expect the script going ahead.
2.2
sleep started
sleep finished
1.2
Parent.sh
が死んだ後、Child.sh
は(SIGINTに関するデフォルトのアクションとして)すぐに終了するはずだと思っていました。
しかし、Parent.sh
はSIGINTを受け取ったようですが、それを無視しました。
なぜこれなのか?
これは、bash
が実装する "Wait and Cooperative Exit" (WCE)が原因で発生します。
Bashが子を待っている間にSIGINT
シグナルを受信すると、子が終了した場合ではなく、そのシグナルが原因で子が死亡した場合にのみbashが自殺します。親プロセスは、子の終了ステータスから、処理されていないシグナルが原因で終了したか終了したかを判断できます。 wait(2)
マンページのWIF*
マクロを参照してください。
より簡単な例:
bash -c 'sh -c "trap exit INT; read p"; echo DONE'
<ctrl-C>
DONE
そして、dash
シェルを使用すると、WCEは実装されません。
dash -c 'sh -c "trap exit INT; read p"; echo DONE'
<ctrl-C>
<no DONE, parent script was killed outright>