【问题标题】:How to wait in bash script to subprocess, if one of them failed so stop everyone如何在 bash 脚本中等待子进程,如果其中一个失败所以停止所有人
【发布时间】:2022-12-16 05:07:59
【问题描述】:

如何在 bash 脚本中等待子进程,如果其中一个返回退出代码 1,那么我想停止所有子进程。

这就是我试图做的。 但是有一些问题:

  1. 如果第一个进程比所有其他进程都长,并且另一个进程在后台失败......那么脚本等待第一个进程完成,即使另一个进程已经失败。

  2. 无法检测到 doSomething 失败,因为我将管道用于所需的打印格式。

        #!/bin/bash
        
        function doSomething()
        {
                echo [ $1 start ]
        
                sleep $1
        
                if [ $1 == 10 ]; then
                        failed
                fi
        
                echo [ sleep $1 ]: done
        }
        
        function failed(){
                        sleep 2
                        echo ------ process failed ------
                        exit 1
        }
        
        function process_log() {
                local NAME=$1
                while read Line; do
                        echo [Name ${NAME}]: ${Line}
                done
        }
        
        pids=""
        
        
        (doSomething 4 | process_log 4)&
        pids+="$! "
        
        (doSomething 17 | process_log 17)&
        pids+="$! "
        
        (doSomething 6 | process_log 6)&
        pids+="$! "
        
        (doSomething 10 | process_log 10)&
        pids+="$! "
        
        (doSomething 22 | process_log 22)&
        pids+="$! "
        
        (doSomething 5 | process_log 5)&
        pids+="$! "
        
        
        for pid in $pids; do
               wait $pid || (pkill -P $$ ; break)
        done
        
        echo done program
    

    有人有想法吗?

【问题讨论】:

  • 不,这不能解决我的问题..谢谢你的帮助! @弗拉瓦多纳
  • 事实上 stackoverflow.com/a/71778627/3387716 基本上可以满足您的需求。只需将 exit 1(您不应该在 failed 函数中包含)替换为对 diex 函数的调用。
  • 它杀死了所有的子进程,也杀死了父进程。我希望父进程中的代码继续运行。在这个例子中,需要打印“完成程序”
  • 好吧,您当前的用例不同,所以这不是重复的问题。检查我的答案,看看它现在是否符合您的要求。
  • 好吧,您当前的用例不是重复问题。检查我更新的答案。

标签: bash shell process multiprocessing sh


【解决方案1】:

它的要点是:

#!/bin/bash
set -m # needed for using negative PIDs
trap '{ kill -- $(jobs -rp | sed s/^/-/); wait; } 2> /dev/null' USR1

doSomething() {
    echo "[ $1 start ]"
    sleep "$1"
    [[ $1 == 10 ]] && failed
    echo "[ sleep $1 ]: done"
}

failed(){
    echo "------ process failed ------" 1>&2
    kill -USR1 "$$"
}

process_log() {
    local name="$1" line
    while IFS='' read -r line; do
        echo "[Name $name]: $line"
    done
}

{ doSomething  4 | process_log  4; } &
{ doSomething 17 | process_log 17; } &
{ doSomething  6 | process_log  6; } &
{ doSomething 10 | process_log 10; } &
{ doSomething 22 | process_log 22; } &
{ doSomething  5 | process_log  5; } &

wait

echo "done program"
[Name 4]: [ 4 start ]
[Name 6]: [ 6 start ]
[Name 17]: [ 17 start ]
[Name 5]: [ 5 start ]
[Name 10]: [ 10 start ]
[Name 22]: [ 22 start ]
[Name 4]: [ sleep 4 ]: done
[Name 5]: [ sleep 5 ]: done
[Name 6]: [ sleep 6 ]: done
------ process failed ------
[Name 10]: [ sleep 10 ]: done
done program
说明

这个想法是让子进程在失败时通知父脚本(使用SIGUSR1信号);主脚本将在收到该信号时终止所有子进程。
但是有一个问题:杀死子进程的 PID 可能还不够,例如当它当前正在运行带有 | 的命令时。在那些情况下你需要杀死整个进程组,这可以通过启用作业控制使用 set -m 并在 kill 命令中使用负 PID。

【讨论】:

  • 谢谢你!!它就像一个魔术(:
【解决方案2】:

GNU 并行--halt-on-error now,1 --tag

对于在每一行前面加上参数的特定process_log,GNU parallel 可以用一个衬里来完成。

安装:

sudo apt install parallel

测试:

myfunc() {
    echo "start: $1"
    i=0
    while [ $i -lt $1 ]; do
      echo "$((i * $1))"
      sleep 1
      i=$((i + 1))
    done
    [[ $1 == 3 ]] && exit 1
    echo "end: $1"
}
export -f myfunc
parallel --lb --halt-on-error now,fail=1 --tag myfunc ::: 1 2 3 4 5

输出:

4       start: 4
4       0
3       start: 3
3       0
1       start: 1
1       0
2       start: 2
2       0
5       start: 5
5       0
1       end: 1
4       4
3       3
2       2
5       5
2       end: 2
4       8
3       6
5       10
parallel: This job failed:
myfunc 3

所以我们看到 45 从未完成,因为 3 在他们之前失败了。每行都以其输入参数为前缀。

GNU parallel 还可以涵盖一些其他常见的前缀用例:

来自Stop bash if any of the functions fail in parallel 的额外评论也适用于此。

在 Ubuntu 22.04 上测试。

【讨论】:

    猜你喜欢
    • 2023-02-21
    • 1970-01-01
    • 2013-09-10
    • 2014-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-27
    相关资源
    最近更新 更多