説明したようにtrap
を使用すると、 on http://linuxcommand.org/wss0160.php#trap ctrl-c(または同様のもの)をキャッチし、終了する前にクリーンアップしてから、返される終了コードを変更します。
これはおそらく現実の世界では違いはありません(たとえば、終了コードは移植可能ではなく、その上に プロセスが終了したときのデフォルトの終了コード で説明されているように常に明確であるとは限らないため)それを防ぐ方法が本当になく、代わりに中断されたスクリプトのデフォルトのエラーコードを返す方法がないのだろうか?
例(bashですが、私の質問はbash固有のものとは見なされません):
#!/bin/bash
trap 'echo EXIT;' EXIT
read -p 'If you ctrl-c me now my return code will be the default for SIGTERM. ' _
trap 'echo SIGINT; exit 1;' INT
read -p 'If you ctrl-c me now my return code will be 1. ' _
出力:
$ ./test.sh # doing ctrl-c for 1st read
If you ctrl-c me now my return code will be the default for SIGTERM.
$ echo $?
130
$ ./test.sh # doing ctrl-c for 2nd read
If you ctrl-c me now my return code will be the default for SIGTERM.
If you ctrl-c me now my return code will be 1. SIGINT
EXIT
$ echo $?
1
(それをよりPOSIX準拠にするために削除するように編集されました。)
(代わりにbashスクリプトにするために編集されましたが、私の質問はシェル固有ではありません。)
ポータブルではない「SIGINT」を支持して、トラップにはポータブルな「INT」を使用するように編集されました。
不要な中括弧を削除し、潜在的なソリューションを追加するために編集されました。
更新:
ハードコードされたいくつかのエラーコードで終了してEXITをトラップすることで解決しました。特定のシステムでは、エラーコードが異なるか、EXITトラップが不可能である可能性がありますが、私の場合は問題ありません。
trap cleanup EXIT
trap 'exit 129' HUP
trap 'exit 130' INT
trap 'exit 143' TERM
EXITハンドラーを変更するだけですinsideクリーンアップハンドラー。次に例を示します。
#!/bin/bash
cleanup() {
echo trapped exit
trap 'exit 0' EXIT
}
trap cleanup EXIT
read -p 'If you ctrl-c me now my return code will be the default for SIGTERM. '
実際、bashの内部read
を中断することは、bashが実行するコマンドを中断することとは少し異なるようです。通常、trap
と入力すると、$?
が設定され、それを保持して同じ値で終了できます。
trap 'rc=$?; echo $rc SIGINT; exit $rc' INT
trap 'rc=$?; echo $rc EXIT; exit $rc' EXIT
sleep
のようなコマンドまたはwait
のような組み込みコマンドを実行しているときにスクリプトが中断された場合、
130 SIGINT
130 EXIT
終了コードは130です。ただし、read -p
、 そうみたいです $?
は0です(とにかく私のバージョンのbash 4.3.42では)。
私のリリースの変更ファイルによると、read
中のシグナルの処理は進行中の可能性があります...(/ usr/share/doc/bash/CHANGES)
このバージョンのbash-4.3-alphaと以前のバージョンのbash-4.2-releaseの間の変更点。
Bashの新機能
r。 Posixモードでは、トラップされた信号によって「読み取り」を中断できます。トラップハンドラーの実行後、readは128以上のシグナルを返し、部分的に読み取られた入力を破棄します。
通常のシグナル終了コードは$?
トラップハンドラへのエントリ時:
sig_handler() {
exit_status=$? # Eg 130 for SIGINT, 128 + (2 == SIGINT)
echo "Doing signal-specific up"
exit "$exit_status"
}
trap sig_handler INT HUP TERM QUIT
別個のEXITトラップがある場合は、同じ方法を使用できます。シグナルハンドラー(存在する場合)から渡された終了ステータスを直ちにクリーンアップし、保存された終了ステータスを返します。
エラーコードを返すだけでは、SIGINTによる終了をシミュレートするには不十分です。これまで誰もこれについて言及しなかったことに驚いています。さらに読む: https://www.cons.org/cracauer/sigint.html
適切な方法は次のとおりです。
for sig in EXIT ABRT HUP INT PIPE QUIT TERM; do
trap "cleanup;
[ $sig = EXIT ] && normal_exit_only_cleanup;
[ $sig != EXIT ] && trap - $sig EXIT && kill -s $sig $$
" $sig
done
これはBash、Dash、zshで動作します。移植性をさらに高めるには、数値信号仕様を使用する必要があります(一方、zshはkill
コマンドの文字列パラメーターを想定しています...)
EXIT
信号の特別な扱いにも注意してください。これは、一部のシェル(つまりBash)が、任意のシグナルに対してEXIT
でトラップを実行するためです(そのシグナルでトラップが定義されている可能性があります)。 EXIT
トラップのリセットはそれを防ぎます。
チェック[ $sig = EXIT ]
は、通常の(シグナルなし)出口でのみコードの実行を許可します。ただし、all信号には、最後にEXIT
でトラップをリセットするトラップが必要です。 normal_exit_only_cleanup
は、そうでないシグナルに対しても呼び出されます。また、set -e
を介して出口によって実行されます。これは、ERR
(Dashではサポートされていません)をトラップし、kill
の前にチェック[ $sig = ERR ]
を追加することで修正できます。
一方、この動作は、Bashで簡単に実行できることを意味します
trap cleanup EXIT
クリーンアップコードを実行して終了ステータスを保持するだけです。
「EXITはすべてをトラップする」という精巧なBashの動作
トラップできないKILLシグナルを削除する
信号名からSIGプレフィックスを削除
kill -s EXIT
を行わないでください
set -e
/ERRを考慮に入れる