web-dev-qa-db-ja.com

EXITとINTをトラップする動作が異なる理由

Kubuntu Trusty64ビットでbash4.3を実行しています。次の2つのファイルを参照してください。

trapping-int.sh

#! /bin/bash
trap "echo Exiting" INT
cat </dev/urandom >/dev/null
echo Hello

trapping-exit.sh

#! /bin/bash
trap "echo Exiting" EXIT
cat </dev/urandom >/dev/null
echo Hello

^ Cを押すと、SIGINTをトラップするとExitingHelloの両方が出力されますが、EXITをトラップするとExitingのみが出力されますが、改行が追加されます。

$ ./trapping-int 
^CExiting
Hello
$ ./trapping-exit 
^CExiting

$

動作が異なる理由を知りたいのですが。また、INTが^ Cで与えられていても、EXITが常に呼び出されると安全に想定できますか?

両方のスクリプトの最後の行をコメントアウトすると、トラップSIGINTはHelloを出力しなくなりますが、EXITをトラップすると余分な改行が出力されます。ここでも理由を知りたいのですが。

ありがとう!

6
jamadagni

まず、シグナルとシェルに関するいくつかの事実の要約:

  • キーボードでCTRL + Cを押すと、フォアグラウンドプロセスのプロセスグループ内のすべてのプロセスにSIGINTが送信されます。この場合、スクリプトを解釈するcatコマンドとbashプロセスの両方がSIGINTを受信することを意味します。

  • INTをトラップすると、ハンドラーで明示的に終了しない限り、INTによってプロセスが終了することはなくなりました。

  • EXITをトラップすると、引数は特定のシグナルではなく、シェルの出口で実行されます。

trapping-int.shの動作は、次のことが起こることがわかっているため、これらの事実を考慮すると簡単です。

  • catプロセスはSIGINTを受け取り、その実行は終了します。
  • bashプロセスはSIGINTを受け取り、そのシグナルハンドラーを実行して、「Exiting\n」をSTDOUTに出力します。
  • bashプロセスは実行を継続し、「Hello\n」をSTDOUTに出力します。
  • bashプロセスは、スクリプトの最後に到達すると終了します。

trapping-exit.shの動作もほとんど簡単です。

  • catプロセスはSIGINTを受け取り、その実行は終了します。
  • bashプロセスはSIGINTを受け取り、シグナルハンドラーも存在しないため、終了します。信号を受信するとすぐに終了するため、echoコマンドを実行/実行/実行しません。
  • bashプロセスが終了しているため、EXITハンドラーが実行され、「Exiting\n」がSTDOUTに出力されます。

残りの質問は「改行はどこから来るのか」です。何が起こっているのかというと、bash自体が改行を出力するSIGINTハンドラーをインストールしていると思います。

trapping-int.shスクリプトでは、SIGINTのBashのハンドラーをオーバーライドするため、余分な改行は取得されません。

9
Steven D