トラップを使用して終了を確認する

トラップを使用して終了を確認する

Ctrl+Cユーザーに確認を求める信号をキャッチしようとしています。キャプチャ部分はうまく機能します。しかし、信号が捕捉されると、通常の実行に戻ることはありません。代わりにスクリプトを終了します。ユーザーがいいえを押したときに実行を再開させるにはどうすればよいですか?

これは私のコードです

hell()
{
echo "Do you want to quit? Press 1 for yes and 0 for no";
read n;
if [ $n == 1 ]; then
exit 1;
fi
}

trap "hell" SIGINT

find /

答え1

どうしたの?

Ctrl+を押すと信号が完全に転送されますCSIGINTフォアグラウンドプロセスグループ。ここでは、findプロセスと呼び出しシェルプロセスに転送されます。find反応はすぐに終了し、シェル反応はトラップを呼び出すことです。

トラップのコードが返されると(つまり、呼び出されないexit)、シグナルによって中断されたコマンドの後に続くコマンドで実行が続行されます。ここでは、コマンドfindの後にスクリプトが終了するので、とにかくスクリプトはすぐに終了します。ただし、別のコマンドを追加すると、入力0と1の違いを確認できます。

find /
echo "find returned $?"

やりたいことをする方法(しかししてはいけないかもしれません)

好きなようにすることができますが、私の答えのこの部分は、実際の問題を解決するよりもシェルプログラミングを見つけることです。

  • 設計上、再起動可能なシグナルは、シェルスクリプトなどの比較的単純なプログラムで通常期待されるシグナルではありません。Ctrl+がCスクリプトを終了すると予想されます。
  • しかし、以下に示すように、これはシェルの機能をわずかに拡張します。

殺人を避けたいならfind背景find / &。その後、使用wait組み込み正常に終了するまで待ちます。シグナルは、wait伝播するシグナルを受信するまでループで実行できる組み込みプログラムを中断します。それからkill

hell () {
  echo "Do you want to quit? Press 1 for yes and 0 for no"
  read n
  if [ "$n" = 1 ]; then
    # Kill the job if it's running, then exit
    if [ -n "$job_pid" ]; then kill $job_pid; fi
    exit 1
  fi
}

job_pid=
trap "hell" SIGINT

# Start a demo job in the background
for i in 1 2 3 4 5; do date; sleep 1; done &
job_pid=$!
# Call wait in a loop; wait will return 0 if the job exits, and 128+$signum if interrupted by a signal.
while ! wait; do
  echo "resuming wait"
done
job_pid=
echo last exit code: $?

このアプローチには、シェルに次の制限があります。

  • 競争条件があります。操作が完了した後にラインの前に+を押すと、シグナルハンドラはシャットダウンを試みますが、Ctrlプロセスはもう存在しません(収穫されたのでゾンビでも)。 ID はすでに他のプロセスで再利用されている可能性があります。これはシェルでは簡単には解決されません(おそらくハンドラを設定して?)。Cjob_pid=$jobpidwaitSIGCHLD
  • wait $job_pidこのフォームは、ジョブの返却ステータスが必要な場合に必要です。ただし、「シグナルによって中断されました」と「シグナルによってジョブが終了しました」を区別することはできませんwait
  • これは複数のサブタスクに簡単には拡張されません。ほとんどのシェル実装の基本を超えて、トラップとシグナルはしばしば驚くべき方法で動作することに注意してください(kshだけがこれをうまく実行します)。

これらの制限を克服するには、PerlやPythonなどの高度な言語を使用してください。

関連情報