Control + C(またはINTR character )子シェルスクリプトの実行中に、SIGINTがフォアグラウンドプロセスグループのすべてのプロセスに送信されます。これには、親プロセスが含まれます。
出典: POSIX.1-2008 XBDセクション11.1.9
このデフォルトの動作をオーバーライドする方法はありますか?子プロセスだけが親に伝播せずに信号を処理することですか?
(ジルの答えに触発されて)
ISIG
フラグが設定されている場合、親がChild
を取得せずにSIGINT
スクリプトがSIGINT
を取得する唯一の方法は、独自のプロセス内にあることです。グループ。これは、_set -m
_オプションで実行できます。
Child
シェルスクリプトで_-m
_オプションをオンにすると、対話型でなくてもジョブ制御が実行されます。これにより、別のプロセスグループで実行され、SIGINT
文字が読み取られたときに親がINTR
を受信できなくなります。
_
-m
_このオプションは、実装がUser Portability Utilitiesオプションをサポートしている場合にサポートされます。すべてのジョブは、独自のプロセスグループで実行されます。バックグラウンドジョブの完了後にシェルがプロンプトを発行する直前に、バックグラウンドジョブの終了ステータスを報告するメッセージが標準エラーに書き込まれます。フォアグラウンドジョブが停止した場合、シェルはその結果を標準エラーにメッセージとして書き込み、ジョブユーティリティによって記述された形式になっています。さらに、ジョブが終了以外のステータスを変更する場合(たとえば、ジョブが入力または出力のために停止した場合、またはSIGSTOPシグナルによって停止した場合)、シェルは次のプロンプトを書き込む直前に同様のメッセージを書き込む必要があります。このオプションは、対話型シェルではデフォルトで有効になっています。
_-m
_オプションは_-i
_に似ていますが、_-i
_ほどシェルの動作を変更しません。
例:
Parent
スクリプト:
_#!/bin/sh
trap 'echo "PARENT: caught SIGINT; exiting"; exit 1' INT
echo "PARENT: pid=$$"
echo "PARENT: Spawning child..."
./Child
echo "PARENT: child returned"
echo "PARENT: exiting normally"
_
Child
スクリプト:
_#!/bin/sh -m
# ^^
# notice the -m option above!
trap 'echo "CHILD: caught SIGINT; exiting"; exit 1' INT
echo "CHILD: pid=$$"
echo "CHILD: hit enter to exit"
read foo
echo "CHILD: exiting normally"
_
これはあなたが打ったときに何が起こるかです Control+CChild
が入力を待っている間:
_$ ./Parent
PARENT: pid=12233
PARENT: Spawning child...
CHILD: pid=12234
CHILD: hit enter to exit
^CCHILD: caught SIGINT; exiting
PARENT: child returned
PARENT: exiting normally
_
親のSIGINT
ハンドラーが決して実行されないことに注意してください。
または、Parent
の代わりにChild
を変更する場合は、次のようにします。
Parent
スクリプト:
_#!/bin/sh
trap 'echo "PARENT: caught SIGINT; exiting"; exit 1' INT
echo "PARENT: pid=$$"
echo "PARENT: Spawning child..."
sh -m ./Child # or 'sh -m -c ./Child' if Child isn't a Shell script
echo "PARENT: child returned"
echo "PARENT: exiting normally"
_
Child
スクリプト(通常; _-m
_は不要):
_#!/bin/sh
trap 'echo "CHILD: caught SIGINT; exiting"; exit 1' INT
echo "CHILD: pid=$$"
echo "CHILD: hit enter to exit"
read foo
echo "CHILD: exiting normally"
_
SIGINT
の間Child
を無視します。これはあなたの質問には対応していませんが、あなたが望むものを手に入れるかもしれません。Child
を次のように変更します:stty -g
_を使用します。stty -isig
_を実行して、INTR
、QUIT
、およびSUSP
文字を含む信号を生成しません。kill -QUIT 0
_ Control+\ 読み取り、_kill -INT $$
_ Control+C 読み込まれます)。これは些細なことではなく、Child
スクリプトまたはそれが実行する何かがインタラクティブであることを意図している場合、これをスムーズに機能させることができない場合があります。EXIT
のトラップから)。stty -isig
_を実行するのではなく、#2と同じ、ユーザーがヒットするのを待つ Enter またはChild
をkillする前の特別でないキー。setpgid()
の呼び出しに使用できる独自のsetpgid
ユーティリティをC、Python、Perlなどで記述します。以下は粗雑なCの実装です:
_#define _XOPEN_SOURCE 700
#include <unistd.h>
#include <signal.h>
int
main(int argc, char *argv[])
{
// todo: add error checking
void (*backup)(int);
setpgid(0, 0);
backup = signal(SIGTTOU, SIG_IGN);
tcsetpgrp(0, getpid());
signal(SIGTTOU, backup);
execvp(argv[1], argv + 1);
return 1;
}
_
Child
の使用例:
_#!/bin/sh
[ "${DID_SETPGID}" = true ] || {
# restart self after calling setpgid(0, 0)
exec env DID_SETPGID=true setpgid "$0" "$@"
# exec failed if control reached this point
exit 1
}
unset DID_SETPGID
# do stuff here
_
POSIXから引用した章で説明されているように、SIGINTはフォアグラウンドプロセスグループ全体に送信されます。したがって、親プログラムの強制終了を回避するには、プログラムを独自のプロセスグループで実行するように調整します。
シェルは組み込みまたは構文の構文を介してsetpgrp
へのアクセスを許可しませんが、シェルをインタラクティブに実行するという間接的な方法があります。 (トリックの StéphaneGimenez に感謝します。)
ksh -ic '
… the part that needs to be interruptible without bothering the parent …
'
まあ、あなたがそれを参照したスタックオーバーフローの質問から、親が信号を処理するように構成する必要があることを明確に述べています。
第2に、POSIXリファレンスには、「ISIGが設定されている場合、INTR文字は処理時に破棄される」と明記されています。
これが2つのオプションです。 3つ目は、独自のプロセスグループで子を実行することです。