【问题标题】:Exit a bash script when one of the subprocesses exits当其中一个子进程退出时退出 bash 脚本
【发布时间】:2016-09-26 14:02:57
【问题描述】:

我在我的脚本中运行了几个后台进程

run_gui()
{
    exec ... # the real commands here
}

run_ai1(), run_ai2 的函数是类似的。

然后我运行函数并做所需的管道

run_gui &
run_ai1 &
run_ai2 &
while true; do
    while true; do
        read -u $ai1_outfd line || echo "Nothing read"
        if [[ $line ]]; then
            : # processing
        fi
    done
    sleep $turndelay
    while true; do
        read -u $ai2_outfd line || echo "nothing read"
        if [[ $line ]]; then
            : # processing
        fi
    done
    sleep $turndelay
done

如果这三个进程中的任何一个退出,我想检查它们的退出代码并终止其余进程。例如,如果run_ai2 以退出代码 3 退出,那么我想停止进程 run_ai1run_gui 并以退出代码 1 退出主脚本。不同背景进程的正确退出代码可能会有所不同。

问题是:我怎样才能检测到它?有命令wait,但我事先不知道哪个脚本会先完成。我可以将 wait 作为后台进程运行 - 但它变得更加笨拙。

你能帮帮我吗?

【问题讨论】:

  • trap SIGCHLD,它允许在子端执行一些语句
  • 你能用Expect吗?在 Expect 中捕获退出的子进程比 POSIX shell 或 Bash 更优雅。
  • @nwk 我应该写一个 bash 脚本。所以我怀疑这是否会被允许。
  • @pasabaporaqui 无论我尝试如何捕获SIGCHLD,都与我在此处描述的相去甚远。进程要么根本不退出(也不对 SIGINT 做出反应),要么过早退出。

标签: bash background-process pid


【解决方案1】:

以下脚本监控测试子进程(在示例中为 sleep+false 和 sleep+true)并报告它们的 PID 和退出代码:

#!/bin/bash

set -m

trap myhandler CHLD

myhandler() {
  echo sigchld received
  cat /tmp/foo
}

( sleep 5; false; echo "exit p1=$?" ) > /tmp/foo &
p1=$!
echo "p1=$p1"

( sleep 3; true; echo "exit p2=$?" ) > /tmp/foo &
p2=$!
echo "p2=$p2"

pstree -p $$
wait

结果是:

p1=3197
p2=3198
prueba(3196)─┬─prueba(3197)───sleep(3199)
             ├─prueba(3198)───sleep(3201)
             └─pstree(3200)
sigchld received
sigchld received
exit p2=0
sigchld received
exit p1=1

使用SIGUSR1 代替SIGCHLD 可能会很有趣;示例见此处:https://*.com/a/12751700/4886927

此外,在陷阱处理程序中,可以验证哪个孩子还活着。比如:

myhandler() {
  if kill -0 $p1; then
    echo "child1 is alive"
  fi 
  if kill -0 $p2; then
    echo "child2 is alive"
  fi 
}

或者当其中一个孩子死亡时杀死两个孩子:

myhandler() {
  if kill -0 $p1 && kill -0 $p2; then
     echo "all childs alive"
  else
    kill -9 $p1 $p2
  fi
}

【讨论】:

  • 这将等待两个进程退出。如果第二个是( yes; echo "exit p2=$?" ) > /tmp/foo &,那么我们的脚本在false 命令停止后不会停止。
  • @marmistrz:当然,现在是时候添加“杀死”命令或阻止其他孩子所需的任何内容了。
  • 好吧,我不应该关注wait。为什么SIGCHLDs 比后台进程多?为什么在命令结束之前收到 sigchld?
  • @marmistrz:这是 sigchld 的问题,脚本使用的任何命令都会触发一个。为此,建议使用 sigusr1。我将编辑答案以寻找替代方案。
最近更新 更多