如何在 SIGCHLD 子失败时处理错误的退出代码

How to handle wrong exit code upon child failure with SIGCHLD

我有一个 shell 脚本,它在后台生成一堆子进程到 运行。如果其中任何一个失败,我想用 kill -- -$$.

终止所有子进程和父进程

我试图在父进程中创建一个更简单的信号处理函数 check_exit_code() 以查看它是否可以正常工作。每次子进程终止时都会调用它,使用 trapSIGCHLD 信号。

#!/bin/sh
set -o monitor

function check_exit_code {
    if [  -eq "0" ]
    then 
        echo "Success: "
    else 
        echo "Fail: "
    fi
}

trap "check_exit_code $?" SIGCHLD
mycommand1 &
mycommand2 &
mycommand3 &
...
wait

不幸的是,这只有 returns Success: 0 即使 mycommand# 失败并且它的退出代码是 2,所以我将函数更改为以下内容。

#!/bin/sh
set -o monitor

function check_exit_code() {
    local EXIT_STATUS=$? 
    if [ "$EXIT_STATUS" -eq "0" ]
    then 
        echo "Success: $EXIT_STATUS"
    else 
        echo "Fail: $EXIT_STATUS"
    fi
}

trap "check_exit_code" SIGCHLD
mycommand1 &
mycommand2 &
mycommand3 &
...
wait

这只能returnFail: 145mycommand#不能return。我怀疑当我写 $? 时,我收到了另一个命令的退出状态。问题是什么?你会如何解决它?

您第一次尝试的问题是 check_exit_status $? 周围的双引号。 shell 在设置陷阱之前将 $? 扩展为零,因此 SIGCHLD 无论如何都会触发 check_exit_status 0,因此常量 Success: 0输出。

关于您的第二次尝试,在进入陷阱操作时,特殊参数 ? 像往常一样保持最近管道的退出状态,在本例中为 wait.由于为 SIGCHLD 设置了陷阱,shell 中断 wait 并将 128+17(SIGCHLD 的数字等价物)分配给 ? 收到那个信号后。


使用 bash-5.1.4 或更高版本,您可以像这样获得所需的结果:

while true; do
  wait -n -p pid
  case $?,$pid in
  ( 0* ) # a job has exited with zero
    continue ;;
  ( *, ) # pid is empty, no jobs left
    break ;;
  ( *  ) # a job has exited with non-zero
    trap "exit $?" TERM
    kill 0
  esac
done