SIGINT が関数呼び出しとサブプロセスを中断するのを防ぐ

SIGINT が関数呼び出しとサブプロセスを中断するのを防ぐ

次のスクリプトを考えてみましょう。

#!/bin/bash

set -o pipefail
set -o history

trapper() {
   func="$1" ; shift
   for sig ; do
      trap "$func $sig" "$sig"
   done
}

err_handler () {
  case $2 in
    INT)
       stop_received=1
    ;;
    TSTP)
    ;;
    ERR)
       if [[ $2 != "INT" ]]; then # for some reason, ERR gets triggered on SIGINT
          code=$?
          if [ $code -ne 0 ]; then
             echo "Failed on line $1"
             echo "$BASH_COMMAND returned $?"
             echo "Content of variables at the time of failure:"
             echo "$(set -o posix; set)"
             exit 1
          fi
       fi
    ;;
  esac
}

main() {
   ping -c 5 www.google.com # this is a test to see if INT interrupts main()
   # do a bunch of stuff, I mean a real bunch of stuff, like check some
   # files, do some processing, connect to a database,
   # do more processing, move some files around, you get the drift
}

exec > >(tee -a my.log)
exec 2>&1

trapper 'err_handler $LINENO' INT TSTP ERR
while main
do
  if [[ "$stop_received" == "1" ]]; then
     break
  fi
  setsid sleep 2 & wait
done
trap ERR

私が達成したいのは、main()関数がゼロ以外の値を返すまで、つまりエラーが発生するかSIGINTが受け取られるまで無限ループでスクリプトを実行することです。

しかし、SIGINTがmain()の実行を停止したくありません。つまり、スクリプトがSIGINTを受け取ると、main()が完了するのを待ってから正常に終了する必要があります。ちなみにCtrl + Cを押すと、pingが中断されることがわかります。現在、私はこれが動作することを確認するためにmain()の下のすべての項目をコメントアウトしています。 pingが中断されたため、main()の下の他のコマンドも中断されるとします。 pingが中断されると、処理は$ stop_received = 1をチェックする行にジャンプし、ループが中断されスクリプトが終了します。割り込みをエコーに置き換えると、スクリプトはデフォルトのwhileループの次の反復を続行します。

SIGINTが現在実行中のコマンドを中断するのを防ぐ方法は?私のスクリプトはデータベースのDMLステートメントを含む多くの操作を実行しているため、main()を中断すると多くの問題が発生する可能性があります。

第二に、スクリプトはCtrl + Zもキャプチャしません。あるいは、スクリプトがctrl + zで停止して終了するには、pidを終了する必要があります。私はsleepがスクリプト自体ではなくbashの子だと思います。ctrl + zはスクリプトを一時停止してスリープを閉じます。だからsetidを設定し、スリープを待つが、それでも中断されます。

ありがとうございます。

答え1

Ctrl+効果を削除する方法はいくつかありますC

  • 信号が発生しないように端末設定を変更してください。
  • ブロックが解除されると、後で信号を転送して信号を保存するために信号をブロックします。
  • シグナルを無視するか、ハンドラを設定します。
  • バックグラウンドプロセスグループでサブプロセスを実行します。

Ctrl+がC押されたことを検出したいので、信号を無視することはオプションではありません。端末設定を変更できますが、その後、カスタムキー入力処理コードを作成する必要があります。シェルはシグナルブロックへのアクセスを提供しません。

ただし、別のプロセスグループで子プロセスを実行すると、子プロセスが信号を自動的に受信しないように分離できます。デフォルトでは、対話型シェルは別のプロセスグループでバックグラウンドコマンドを実行しますが、非対話型シェルは同じプロセスグループで実行され、フォアグラウンドプロセスグループ内のすべてのプロセスは端末イベントからシグナルを受け取ります。シェルに言う別のプロセスグループでバックグラウンドジョブを実行するには、次の手順を実行します。set -m。実行は、別のプロセスグループで強制的に実行するsetsid ping …別の方法です。ping

set -m
interrupted=
trap 'echo Interrupted, but ping may still be running' INT
set -m
ping … &
while wait; [ $? -ge 128 ]; do echo "Waiting for background jobs"; done
echo ping has finished

Ctrlバックグラウンドプロセスグループを中断するには、Zシェルから信号を伝播する必要があります。

シェルスクリプトでは細かい信号制御がやや難しく、ATT ksh以外のシェルでは特殊なケースが発生したときに問題が発生する傾向があるため、Perl、Python、Rubyなど、より多くの制御を提供する言語を使用することをお勧めします。

答え2

割り込みルーチンを無効にします。

トラップ "" ERR HUP INT QUIT TERM TSTP TTIN TTOU

ただし、サブコマンドがトラップ自体を処理し、コマンドが実際に完了する必要がある場合は、信号が転送されるのを防ぐ必要があります。

追加のコマンドのインストールに気にしないLinuxユーザーの場合は、次のものを使用できます。

待つ[注文する]

または調整することができます。最新のソースコードをお待ちください必要に応じてプログラムに追加するか、次のコードを使用してください。ザイルズの答え。欠点は、アップストリームアップデートの利点を享受できないことです。

他の端末とサービス管理者はまだ「コマンド」を終了できます。サービス管理者が「コマンド」を閉じることができないようにするには、適切な終了モードと終了信号セットを使用してそのコマンドをサービスとして実行する必要があります。

関連情報