trap
trap ... INT TERM EXIT
クリーニング作業の多くの例。しかし、3つのシグスペックをすべてリストすることは本当に必要ですか?
SIGNAL_SPECがEXIT(0)ARGの場合、シェルを終了したときに実行されます。
SIGINT
これは、スクリプトが正常に完了したか、または受け取ったために適用されると思いますSIGTERM
。実験を通して私の信念も確認された。
$ cat ./trap-exit
#!/bin/bash
trap 'echo TRAP' EXIT
sleep 3
$ ./trap-exit & sleep 1; kill -INT %1
[1] 759
TRAP
[1]+ Interrupt ./trap-exit
$ ./trap-exit & sleep 1; kill -TERM %1
[1] 773
TRAP
[1]+ Terminated ./trap-exit
それでは、なぜそれほど多くの例がそれらをすべてリストするのでしょうかINT TERM EXIT
。それとも私が何かを見逃しているのか、EXIT
足の裏がそれを見逃す場合があるのか?
答え1
はい、少し違います。
EnterSIGINT
このスクリプトは押すか送信すると終了しますSIGTERM
。
trap '' EXIT
echo ' --- press ENTER to close --- '
read response
[次へ]をクリックするとスクリプトが終了しますEnter。
trap '' EXIT INT TERM
echo ' --- press ENTER to close --- '
read response
*テスト対象シェン、吹くとジッシュ。 (これ以上適用されません。シェントラップ実行コマンドを追加するとき)
そして@Shawnが言ったことは次のとおりです。禁煙健康増進協会そしてスプリントキャプチャ信号を使用しないでくださいEXIT
。
EXIT
したがって、強力な信号処理のために完全に捕捉するのを避け、次のものを使用することをお勧めします。
cleanup() {
echo "Cleaning stuff up..."
exit
}
trap cleanup INT TERM
echo ' --- press ENTER to close --- '
read var
cleanup
答え2
これPOSIX仕様EXITトラップが実行される条件の説明はあまりありませんが、実行時に環境がどのように見えるかを説明します。
Busyboxのashシェルでは、トラップ終了テストはSIGINTまたはSIGTERMのため終了する前に「TRAP」をエコーしません。このように動作しない他のシェルがあると思います。
# /tmp/test.sh & sleep 1; kill -INT %1
#
[1]+ Interrupt /tmp/test.sh
#
#
# /tmp/test.sh & sleep 1; kill -TERM %1
#
[1]+ Terminated /tmp/test.sh
#
答え3
問題があるため、最後の回答を改善するには:
# Our general exit handler
cleanup() {
err=$?
echo "Cleaning stuff up..."
trap '' EXIT INT TERM
exit $err
}
sig_cleanup() {
trap '' EXIT # some shells will call EXIT after the INT handler
false # sets $?
cleanup
}
trap cleanup EXIT
trap sig_cleanup INT QUIT TERM
上記は次のとおりです。
テスト中にINTおよびTERMハンドラは終了しません。エラーを処理した後、シェルは終了ステータスを返します(驚きではありません)。そのため、清掃後は必ず終了をさせ、信号がある場合は常にエラーコードを使用します(その他正常終了の場合はエラーコードを保持します)。
Bashを使用すると、INTハンドラで終了するとEXITハンドラも呼び出されるようになり、終了ハンドラを解放して直接呼び出しました(動作に関係なくすべてのシェルで動作します)。
シェルスクリプトが一番下に到達する前にシャットダウンできるため、シャットダウンをキャッチします。構文エラー、-e、および0以外の戻り値の設定、単に終了を呼び出します。理解するためにシェルスクリプトに頼ることはできません。
一度試したことがない場合は、SIGQUITはCtrl-\です。追加のコアダンプを提供します。だから少しぼやけていても入れておくのも価値があると思います。
過去の経験によれば、私のように常にCtrl-Cを数回押すと、シェルスクリプトのクリーンアップ部分を途中で捉えることができます。 。
答え4
達成する目標とターゲットシェルによって異なります。bash
たぶんそれだけを使うことができるからですEXIT
。しかし、すべての殻ではありませんEXIT
/でハンドラを呼び出します。SIGINT
SIGTERM
これを行うには、複数の信号に対してハンドラ(trap '...' INT EXIT
)を設定しますが、複数回呼び出すことができます。
$ bash -c 'trap "echo trap" INT EXIT; sleep 3' & pid=$!; sleep 1; kill -INT $pid; wait
[1] 276923
trap
trap
[1]+ Done bash -c 'trap "echo trap" INT EXIT; sleep 3'
したがって、文章を書くときにこの点を念頭に置いたり試したりすることができます。今後EXIT
すべてをハンドラに委ねる:
$ bash -c 'trap "exit 123" INT; trap "echo EXIT \$?" EXIT; sleep 3' & pid=$!; sleep 1; kill -INT $pid; wait
[1] 286229
EXIT 123
[1]+ Exit 123 bash -c 'trap "exit 123" INT; trap "echo EXIT \$?" EXIT; sleep 3'
SIGINT
しかし、次のハンドラを構築すると、キルスクリプトSIGINT
:
a.sh
:
trap 'exit 123' INT
trap 'echo EXIT $?; trap - INT; kill -INT $$' EXIT
sleep 3
$ bash h.sh & pid=$!; sleep 1; kill -INT $pid; wait $pid
[1] 236263
EXIT 123
[1]+ Interrupt bash h.sh
そしてDebian < 10(dash < 0.5.10
)スクリプトを終了するように指示する(そうであれば)通過できない。
私が思いついた解決策は次のとおりです。
set -eu
cleanup() {
echo "cleanup ($1)"
trap - INT TERM EXIT # avoid reexecuting handlers
if [ "$1" = 130 ]; then
kill -INT $$
elif [ "$1" = 143 ]; then
kill -TERM $$
else
exit "$1"
fi
}
trap 'cleanup 130' INT
trap 'cleanup 143' TERM
trap 'cleanup $?' EXIT
if [ "${1-}" = fail ]; then
no-such-command
fi
sleep 3
$ bash f.sh; echo $?
cleanup (0)
0
$ bash f.sh fail; echo $?
f.sh: line 20: no-such-command: command not found
cleanup (127)
127
$ bash f.sh & pid=$!; sleep 1; kill -INT $pid; wait $pid
[1] 282422
cleanup (130)
[1]+ Interrupt bash f.sh
$ bash f.sh & pid=$!; sleep 1; kill -TERM $pid; wait $pid
[1] 282458
cleanup (143)
[1]+ Terminated bash f.sh
テスト対象:
bash
:5.1.8
- ダッシュ:
0.5.10
,0.5.8
,0.5.7
- Alpine Linux 3.14(
busybox
)