次のようなbashスクリプトがあるとします。
while :
do
foo
done
このスクリプトをコンソールから実行し、2回のfooの実行の間に発生する限り、任意の時間にスクリプトを終了できるようにしたいと思います。したがって、たとえば、私が Ctrl+C (スクリプトを終了させる別のアクションである可能性があります。 Ctrl+C 単なる例です)fooが実行された後、次の利用可能なポイントで終了します
while :
do
foo
if [pressed_ctrl_c]:
break
done
次のような構成を試すことができます。
#!/bin/bash
#
INTR=
trap 'INTR=yes; echo "** INTR **" >&2' INT
while :
do
(
# Protect the subshell block
trap '' INT
# Protected code here
echo -n "The date/time is: "
sleep 2
date
read -t2 -p 'Continue (y/n)? ' YN || echo
test n = "$YN" && echo "Asked for BREAK" >&2 && exit 90
)
SS=$?
test 90 -eq $SS && echo "Matched BREAK" >&2 && break
# Ctrl/C, perhaps?
test yes = "$INTR" && echo "Matched INTR" >&2 && break
done
exit 0
注意事項
read
とtest
のペアは、( ... )
ブロック内の保護されたコードセグメントへのインタラクティブな制御を示しています。exit 90
はbreak
と同等ですが、サブシェルの内部からのものです。サブシェルブロックが終了した直後のtest 0 != $? ...
行は、exit 90
ステータスをキャプチャし、コードが実際に必要としたbreak
を実装するためにあります。break
、exit
など)を示すことができます。gdb
はSIGINT
の独自のハンドラーをインストールします(CtrlC)。ユーザーがセッションから抜け出さないようにすることが目的の場合、割り込みキーを変更すると、状況がわかりにくくなる可能性があります(以下のコードを参照)。エレガントではありませんが、潜在的に効果的です。端末のSIGINTキーを変更する
G=$(stty -g) # Save settings
test -n "$G" && stty intr ^A # That is caret and A, not Ctrl/A
# ... SIGINT generated with Ctrl/A rather than Ctrl/C ...
test -n "$G" && stty "$G" # Restore original settings
これはうまくいくようです:
#!/bin/sh
pressed_ctrl_c=
trap "pressed_ctrl_c=1" INT
while true
do
(trap "" INT; foo)&
wait || wait
if [ "$pressed_ctrl_c" ]
then
# echo
break
fi
done
pressed_ctrl_c
をnullに初期化します。これはおそらくスクリプトでは必要ありません。trap command signum
は、信号番号をキャッチするように設定するようにシェルに指示しますsignum
そしてそれがキャッチしたときにcommand
を実行します。 「INT」は「SIGINT」の略で、「interruptsignal」の略です。これはによって生成される信号の専門用語です。 Ctrl+Cなので、入力すると Ctrl+C、シェルはpressed_ctrl_c
を1に設定します(SIGINTの数値は2なので、タイピングを節約したい場合はtrap "pressed_ctrl_c=1" 2
と言うことができます)。trap
コマンドを使用します。今回はcommand
はnull文字列です。これにより、シェルは信号番号を無視するようになりますsignum
。これは括弧内にあるため、サブシェルにのみ影響します。foo
を実行します。サブシェルから実行されたため、foo
は無視します Ctrl+Cつまり、入力しても実行され続けます。wait
コマンドが成功した場合は、if
ステートメントに進みます。失敗した場合は、別のを実行します。 (これに戻ります。)$pressed_ctrl_c
が設定されている場合は、ループを抜けます。必要に応じて、echo
コマンドのコメントを解除します。 Ctrl+C ターミナルに^C
と表示され、次の行に移動します。コマンドをバックグラウンドで実行し、すぐにwait
を実行します。これは、フォアグラウンドでコマンドを実行するのとよく似ています(少なくとも、スクリプトで実行した場合)。 wait
コマンドは、サブシェルが終了すると正常に終了します。つまり、foo
コマンドが終了したときです。 (wait
がエラーを返しても、foo
コマンドは正常に終了します。)最初のwait
コマンドが正常に終了すると、2番目のコマンドをスキップしてif
。
ループを実行しているシェルは割り込みをキャッチしていますが、サブシェル、つまりfoo
プロセスは割り込みを無視しています。したがって、入力すると Ctrl+C、シェルはpressed_ctrl_c
を1に設定し、(最初の)wait
コマンドを中止します。最初のwait
コマンドが失敗したため、2番目のコマンドに進みます。 foo
サブシェルはまだ実行中であるため、このwait
にはまだ処理が必要です(つまり、foo
が完了するまで待機します)。
最後に、変数が次のことを示すように設定されている場合 Ctrl+Cfoo
の実行中に押された場合、ループの出力を中断します。
を押すと Ctrl+C 2回、2番目のコマンドは2番目のwait
コマンドを中断して中止します。これにより、foo
をバックグラウンドで実行したまま、スクリプトが終了してシェルプロンプトに戻ります。これを軽減するには、wait || wait || wait || wait
と言います。必要なだけ拡張します。入力する必要があります Ctrl+Cwait
ごとに1回、スクリプトを途中で終了します。
この問題は、スクリプトによってバックグラウンドに置かれるプロセスの標準入力が/dev/null
に設定されていることです。 foo
がキーボードから読み取られる場合は、上記を修正する必要があります。