SIGINTなどをトラップするときに終了コードを保持しますか?


14

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

スクリプトは少し奇妙に見えます。read現在のコプロセスから読み取るように指示するtrap cmd SIGINTと、標準を使用する必要があると動作しないためtrap cmd INTです。
schily 2015年

はい、もちろん、POSIXではSIGプレフィックスがありません。
2015年

おっと、でも "read -p"もサポートされないので、bashに適合させます。
2015年

@schily:「コプロセス」とはどういう意味かわかりません。
2015年

まあ、Korn Shellのmanページにはread -p、現在のコプロセスから入力を読み取ると書かれています。
2015年

回答:


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. '

もしかしてtrap cleanup INT代わりにtrap cleanup EXIT
Jeff Schaller

EXITを意味していたと思います。最後にexitが呼び出されると、トラップが終了してreturn 0で終了するようにトラップが変更されます。シグナルハンドラーが再帰的ではないようです。
岩だらけの

OK、あなたの例では、私は(SIG)INTをまったくトラップしていません。これは、ユーザーがctrl-cを使用してインタラクティブスクリプトを終了した場合でも、クリーンアップを実行するために実際に必要なものです。
2015年

@phk OK。私はあなたが出口を強制的に0または私が収集したことが問題であった他のいくつかの値を返すようにする方法のアイデアを得たと思いますが。SIGINTによって呼び出される実際のクリーンアップハンドラー内にトラップのEXIT設定を配置するようにコードを調整できると思います。
岩だらけの

@rocky:私の目標は、ハンドラーなしで通常持っていた終了コードを変更せずに、トラップハンドラーを用意することでした。これで、通常取得する終了コードを簡単に返すことができますが、問題は、これらの場合の正確な終了コードがわからないことでした(私がリンクしたスレッドとmeuhからの投稿に見られるように、非常に複雑です)。あなたの投稿はまだ役に立ちましたが、トラップハンドラーを動的に再定義することは考えていませんでした。
2015年

9

実際、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の間の変更点。

  1. Bashの新機能

    r。Posixモードでは、トラップされた信号によって「読み取り」を中断できます。トラップハンドラを実行した後、readは128以上のシグナルを返し、部分的に読み取られた入力を破棄します。


OK、それは本当に奇妙です。これはbash 4.3.42(cygwinのもとで)のインタラクティブシェルでは130、スクリプトとPOSIXモードでは同じシェルの下では0で違いはありません。しかし、その後ダッシュとbusyboxの下でそれは常に1です
PHK

終了しないトラップを使用してPOSIXモードを試しreadましたが、(私の回答に追加された)CHANGESファイルに記載されているように、が再起動します。したがって、それは進行中の作業かもしれません。
meuh

終了コード130は100%移植不可能です。Bourne Shellは128 + signoシグナルの終了コードとして使用しますが、ksh93はを使用し256 + signoます。POSIXは言う:128を超える何か....
2015年

@schily True、元の投稿でリンクしたスレッド(unix.stackexchange.com/questions/99112)に記載されています。
2015年

6

$?トラップハンドラに入ると、通常のシグナル終了コードが利用可能になります。

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トラップがある場合は、同じ方法を使用できます。シグナルハンドラー(存在する場合)から渡された終了ステータスをすぐにクリーンアップし、保存された終了ステータスを返します。


これはほとんどのシェルに適用されますか?非常に素晴らしい。localただし、POSIXではありません。
phk 2018

1
編集。以外ではテストしていません{ba,z}sh。私の知る限り、それは関係なく行うことができる最高です。
トム・ヘイル

これはダッシュでは機能しません($?受信信号が何であっても1です)。
MoonSweep

2

エラーコードを返すだけでは、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ないシグナルに対しても呼び出されます。また、を介してexitによって実行されset -eます。これは、ERR(Dashでサポートされていない)でトラップし、の[ $sig = ERR ]前にチェックを追加することで修正できますkill

簡略化されたBashのみのバージョン

一方、この動作は、Bashで簡単に実行できることを意味します

trap cleanup EXIT

クリーンアップコードを実行して終了ステータスを保持するだけです。

編集済み

  • 「EXITはすべてをトラップする」という精巧なBashの動作

  • トラップできないKILLシグナルを削除する

  • 信号名からSIGプレフィックスを削除

  • しようとしないでください kill -s EXIT

  • テイクset -e/ ERRを考慮に


シグナルをトラップするプログラムが、シグナルを受信したという事実を維持する方法で終了するという一般的な要件はありません。たとえば、プログラムは送信SIGINTがシャットダウンの方法であることを宣言し、エラーなしで終了した場合は0で終了するか、正常にシャットダウンしなかった場合は別のエラーコードで終了することを決定します。適例:top。実行top; echo $?してからCtrl-Cを押します。画面上にダンプステータスは0になります。
ルイ

1
指摘いただきありがとうございます。ただし、ポスターは、終了コードの保持について具体的に尋ねました。
philipp2100
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.