Bash

由於信號,bash不會在子程序異常退出時退出

  • February 17, 2016

我真的很難理解我做錯了什麼,為什麼?

我有一個launch.sh啟動腳本process.sh

launch.sh

#!/bin/bash
while true; do 
./process.sh 
done

process.sh

#!/bin/bash
function signalHandler() {
   for i in {1..2}; do
       sleep 0.1s
       echo "process.sh: cleanup $i"
   done
   exit 130
}

trap "signalHandler" "SIGINT"

while true; do 
sleep 1s
done

當我跑步時

./launch.sh &

然後用

kill -s SIGINT -$!    

where$!獲取最後一個命令(launch.sh)的 PI​​D,減號將信號發送給所有子程序,然後launch.sh繼續。為什麼?


我期望以下行為(根據此部落格Signal & Bash):

腳本launch.sh在後台執行的 shell A 被中斷,Bash 等到process.sh完成。由於process.sh中的信號處理程序導致異常返回(退出 130)process.sh,shell A 應該退出。為什麼不呢?

如果該子程序的狀態表明它由於該信號而異常退出,則 shell 會進行清理,刪除其信號處理程序,並再次殺死自身以觸發作業系統預設操作(異常退出)。或者,它執行設置為陷阱的腳本信號處理程序,然後繼續。

首先,exit 130不是異常退出。是正常退出,退出狀態為130。從man 3 wait(POSIX)可以看出:

  If  the  information  pointed  to  by  stat_loc was stored by a call to
  waitpid()  that  specified  the  WUNTRACED     and  WCONTINUED   flags,
  exactly one of the macros WIFEXITED(*stat_loc), WIFSIGNALED(*stat_loc),
  WIFSTOPPED(*stat_loc),  and WIFCONTINUED(*stat_loc)  shall evaluate  to
  a non-zero value.

WIFEXITED檢查正常退出,並WIFSIGNALLED由於未擷取的信號而終止。由於這些是互斥的,exit 130因此是正常的。

當程序被 SIGINT 終止時,退出狀態為 130 是因為 bash 將其設置為程序外部的 130,因為它檢測到由於 SIGINT 導致的退出:為什麼 bash 設置 $?(退出狀態)在 Ctrl-C 或 Ctrl-Z 上非零?

其次,處理 SIGINT 然後死亡的程序應該用 SIGINT 殺死自己。Greg 的 wiki (一個很好的 shell 資源)對此有一個註釋

如果您選擇為 SIGINT 設置處理程序(而不是使用 EXIT 陷阱),您應該知道響應 SIGINT 退出的程序應該使用 SIGINT而不是簡單地退出,以避免對其呼叫者造成問題。因此:

trap 'rm -f "$tempfile"; trap - INT; kill -INT $$' INT

現在,如果您將其更改exit 130為:

trap - INT
kill -INT $$

你會看到預期的行為。

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