キーストロークを使用してBashスクリプトから呼び出しタイムアウトを削除できないのはなぜですか?

キーストロークを使用してBashスクリプトから呼び出しタイムアウトを削除できないのはなぜですか?

[編集:これは、生成されたすべてのプロセスを終了する方法を尋ねる他の質問と似ています。答えはすべてpkillを使用しているようです。だから私の質問の中核はおそらく:スクリプトによって生成されたすべてのプロセスにCtrl-C / Zを伝播する方法はありますか? ]

coreutilsのコマンドを使用してSoXを呼び出す場合(議論rectimeoutここ)、Bashスクリプトから呼び出されると、キーストロークでそれを終了する方法がないようです。

例:

timeout 10 rec test.wav

Ctrl... bashで+CまたはCtrl+を使用して終了できますが、Zスクリプト内で呼び出すときはそうではありません。

timeout 10 ping nowhere

Ctrl... bashで+CまたはCtrl+で終了することができますZ。 +はスクリプト内で実行されるときですCtrlZ

プロセスIDを見つけて終了できますが、標準の割り込みキーを押すことができないのはなぜですか?私のスクリプトを設定する方法はありますか?

答え1

Ctrl+シグナルキーがCフォアグラウンドのすべてのプロセスにシグナルを送信するのを待ちます。プロセスグループ

通常、プロセスグループはパイプです。たとえば、head <somefile | sort実行中のプロセスheadと実行中のプロセスはsortシェルと同じプロセスグループにあるため、両方がシグナルを受け取ります。somecommand &バックグラウンドでジョブを実行している場合()ジョブは独自のプロセスグループにあるため、Ctrl+を押してもC影響を受けません。

プログラムtimeoutは自分自身を独自のプロセスグループに配置します。ソースコードから:

/* Ensure we're in our own group so all subprocesses can be killed.
   Note we don't just put the child in a separate group as
   then we would need to worry about foreground and background groups
   and propagating signals between them.  */
setpgid (0, 0);

タイムアウトが発生した場合は、timeoutそのグループが属するプロセスグループを終了する簡単な回避策を実行してください。自分を別のプロセスグループに入れたので、親プロセスはそのグループに属しません。ここでプロセスグループを使用すると、サブアプリケーションが複数のプロセスに分岐した場合、すべてのプロセスがシグナルを受け取ります。

timeoutコマンドラインから直接実行して+を押すと、子CtrlプロセスはC結果SIGINTを受け取りますが、親の対話型シェルはそうではありません。スクリプトから呼び出されると、スクリプトを実行しているシェルだけがシグナルを受け取ります。別のプロセスグループにあるため、信号を受信しません。timeouttimeouttimeouttimeout

組み込み関数を使用して、シェルスクリプトでシグナルハンドラを設定できますtrap。残念ながら、それほど単純ではありません。考えてみてください:

#!/bin/sh
trap 'echo Interrupted at $(date)' INT
date
timeout 5 sleep 10
date

Ctrl2秒後に+を押すと、Cまだ5秒待ってから「Interrupted」メッセージが表示されます。これは、バックグラウンドジョブがアクティブな間にシェルがトラップコードを実行しないためです。

この問題を解決するには、バックグラウンドでタスクを実行してください。シグナルハンドラでは、killシグナルをtimeoutプロセスグループに転送するための呼び出しが行われます。

#!/bin/sh
trap 'kill -INT -$pid' INT
timeout 5 sleep 10 &
pid=$!
wait $pid

答え2

Gilesが提供した素晴らしい答えに基づいて構築されました。 timeout コマンドには前景オプションがあります。 CTRL+C を使用すると、timeout コマンドが終了します。

#!/bin/sh
trap 'echo caught interrupt and exiting;exit' INT
date
timeout --foreground 5 sleep 10
date

答え3

デフォルトでは、Ctrl+は信号をC送り、+Zは信号を送ります。SIGINTCtrlSIGTSTP

SIGTSTPプロセスを停止するとSIGCONT続行します。

これは、コマンドラインから分岐したフォアグラウンドプロセスで機能します。

プロセスがバックグラウンドプロセスの場合は、その信号をプロセスに別の方法で送信する必要があります。killそうします。理論的には、終了時に "-"演算子は子プロセスにもシグナルを送信する必要がありますが、期待どおりに動作することはほとんどありません。

追加資料:Unix信号

関連情報