trap
の多くの例では、クリーンアップタスクにtrap ... INT TERM EXIT
を使用しています。しかし、3つのsigspecをすべてリストする必要があるのでしょうか。
マニュアルは言う:
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
が見逃すケースはありますか?
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
#
はい、違いがあります。
このスクリプトは、を押すと終了します Enter、または送信SIGINT
またはSIGTERM
:
trap '' EXIT
echo ' --- press ENTER to close --- '
read response
このスクリプトは、を押すと終了します Enter:
trap '' EXIT INT TERM
echo ' --- press ENTER to close --- '
read response
*sh、Bash、およびZshでテストされています。 (トラップを実行するコマンドを追加すると、shでは動作しなくなります)
AshとDashはEXIT
でシグナルをトラップしません。
したがって、信号をロバストに処理するには、EXIT
をまったくトラップしないようにし、次のようなものを使用するのが最善です。
cleanup() {
echo "Cleaning stuff up..."
exit
}
trap cleanup INT TERM
echo ' --- press ENTER to close --- '
read var
cleanup
それが問題を持っているので、最後の答えを洗練する:
# 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ハンドラーも呼び出されるようです。そのため、出口ハンドラーのトラップを解除して自分で呼び出します(これは、動作に関係なくすべてのシェルで機能します)。
シェルスクリプトが最後に到達する前に終了する可能性があるため、私は終了をトラップします。一番下に行くシェルスクリプトに依存することはできません。
SIGQUITは、一度も試したことがない場合はCtrl- \です。ボーナスコアダンプを取得します。ですから、少しあいまいであっても、トラッピングする価値があると思います。
過去の経験では、(私と同じように)Ctrl-Cを常に数回押すと、シェルスクリプトのクリーンアップ部分の途中でそれが見つかる場合があるため、これは機能しますが、必ずしも完全に機能するとは限りません。