説明されているように使用する場合trap
、たとえば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
答え1
実際、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です。しかし、0のようread -p
です$?
(とにかく私のbashバージョン4.3.42では)。
私のバージョンの変更ファイルによっては、信号処理が進行中である可能性がread
あります...(/usr/share/doc/bash/CHANGES)
この bash-4.3-alpha バージョンと以前のバージョンの bash-4.2-release 間の変更点です。
バッシュの新機能
Posixモードでは、「読み取り」がキャプチャ信号によって中断される可能性があります。トラップハンドラを実行した後、readは128+signalを返し、部分的に読み取られた入力を削除します。
答え2
$?
トラップハンドラに入ると、一般的な信号終了コードの1つを使用できます。
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トラップがある場合は、同じアプローチを使用できます。つまり、シグナルハンドラ(存在する場合)から渡された終了ステータスをすぐに消去し、保存された終了ステータスを返します。
答え3
単にいくつかのエラーコードを返すだけでは、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 ]
により、コードは通常の(信号なし)シャットダウンでのみ実行できます。しかし、みんな信号にはトラップが必要です。最後にトラップがリセットされますEXIT
。normal_exit_only_cleanup
存在しない信号に対しても呼び出されます。を終了しても実行されますset -e
。これはキャプチャERR
(Dashではサポートされていません)し、実行することで[ $sig = ERR ]
実行できますkill
。
Bashの簡易版
一方、この動作は Bash では単に次のことができることを意味します。
trap cleanup EXIT
クリーンアップコードを実行して終了状態を維持します。
編集済み
Bashの「EXITキャプチャすべて」の動作の詳細
キャプチャできないKILLシグナルの削除
シグナル名からSIGプレフィックスを削除する
試してはいけない
kill -s EXIT
set -e
/ ERRを考えてください。
答え4
EXITハンドラを変更するだけです。~へあなたのクリーンアップハンドラ。例は次のとおりです。
#!/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. '