Shell-Script

防止 SIGINT 中斷函式呼叫和子程序

  • February 12, 2021

考慮以下腳本:

#!/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 會導致腳本暫停,從而使 sleep 陷入困境。因此 setid 並等待睡眠,但它仍然掛起。

謝謝。

有幾種方法可以切斷Ctrl+的效果C

  • 更改終端設置,使其不產生信號。
  • 阻塞信號,以便在信號解除阻塞時將其保存以供以後傳送。
  • 忽略信號,或為其設置處理程序。
  • 在後台程序組中執行子程序。

由於您想檢測Ctrl+C已被按下,因此忽略信號已輸出。您可以更改終端設置,但您需要編寫自定義密鑰處理程式碼。Shell 不提供對信號阻塞的訪問。

但是,您可以通過在單獨的程序組中執行子程序來自動將子程序與接收信號隔離開來。預設情況下,互動式 shell 在單獨的程序組中執行後台命令,但非互動式 shell 在同一程序組中執行它們,並且前台程序組中的所有程序都會接收來自終端事件的信號。要告訴 shell在單獨的程序組中執行後台作業,請執行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暫停一個後台程序組,你需要從 shell 傳播信號。

精細地控制信號對於 shell 腳本來說有點牽強,而 ATT ksh 以外的 shell 在遇到極端情況時往往會出現一些問題,因此請考慮使用一種可以為您提供更多控制的語言,例如 Perl、Python 或 Ruby。

引用自:https://unix.stackexchange.com/questions/156698