如何在bash脚本中等待从该脚本派生的几个子进程完成,然后在任何子进程以code !=0结束时返回退出代码!=0?

简单的脚本:

#!/bin/bash
for i in `seq 0 9`; do
  doCalculations $i &
done
wait

上面的脚本将等待所有10个子进程,但它总是给出退出状态0(参见help wait)。我如何修改这个脚本,以便它将发现衍生子进程的退出状态,并在任何子进程以code !=0结束时返回退出代码1 ?

有没有比收集子进程的pid、按顺序等待它们并求和退出状态更好的解决方案呢?


当前回答

我有一个类似的情况,但有各种各样的问题与循环子shell,确保这里的其他解决方案不能工作,所以我让我的循环编写脚本,我将运行,等待结束。有效:

#!/bin/bash
echo > tmpscript.sh
for i in `seq 0 9`; do
    echo "doCalculations $i &" >> tmpscript.sh
done
echo "wait" >> tmpscript.sh
chmod u+x tmpscript.sh
./tmpscript.sh

愚蠢,但简单,并帮助调试一些事后的事情。

如果我有时间,我会更深入地了解GNU并行,但这对我自己的“doCalculations”过程来说很困难。

其他回答

我刚刚修改了一个脚本到后台和并行化的过程。

我做了一些实验(在Solaris上使用bash和ksh),发现如果退出状态不为零,'wait'将输出退出状态,或者当没有提供PID参数时,将输出一个返回非零退出的作业列表。如。

Bash:

$ sleep 20 && exit 1 &
$ sleep 10 && exit 2 &
$ wait
[1]-  Exit 2                  sleep 20 && exit 2
[2]+  Exit 1                  sleep 10 && exit 1

Ksh:

$ sleep 20 && exit 1 &
$ sleep 10 && exit 2 &
$ wait
[1]+  Done(2)                  sleep 20 && exit 2
[2]+  Done(1)                  sleep 10 && exit 1

这个输出被写入stderr,所以OPs示例的简单解决方案可以是:

#!/bin/bash

trap "rm -f /tmp/x.$$" EXIT

for i in `seq 0 9`; do
  doCalculations $i &
done

wait 2> /tmp/x.$$
if [ `wc -l /tmp/x.$$` -gt 0 ] ; then
  exit 1
fi

虽然这:

wait 2> >(wc -l)

也将返回一个计数,但不包含TMP文件。这也可以这样使用,例如:

wait 2> >(if [ `wc -l` -gt 0 ] ; then echo "ERROR"; fi)

但是这并不比tmp文件有用多少。我找不到一种有效的方法来避免tmp文件,同时也避免在子shell中运行“等待”,这根本不会起作用。

简单地说:

#!/bin/bash

pids=""

for i in `seq 0 9`; do
   doCalculations $i &
   pids="$pids $!"
done

wait $pids

...code continued here ...

更新:

正如许多评论者指出的那样,上面的代码在继续之前会等待所有进程完成,但如果其中一个进程失败了,它不会退出,也不会失败,可以通过@Bryan, @SamBrightman和其他人建议的以下修改来完成:

#!/bin/bash

pids=""
RESULT=0


for i in `seq 0 9`; do
   doCalculations $i &
   pids="$pids $!"
done

for pid in $pids; do
    wait $pid || let "RESULT=1"
done

if [ "$RESULT" == "1" ];
    then
       exit 1
fi

...code continued here ...

我已经尝试过了,并结合了其他例子中最好的部分。该脚本将在任何后台进程退出时执行checkpid函数,并输出退出状态而不诉诸轮询。

#!/bin/bash

set -o monitor

sleep 2 &
sleep 4 && exit 1 &
sleep 6 &

pids=`jobs -p`

checkpids() {
    for pid in $pids; do
        if kill -0 $pid 2>/dev/null; then
            echo $pid is still alive.
        elif wait $pid; then
            echo $pid exited with zero exit status.
        else
            echo $pid exited with non-zero exit status.
        fi
    done
    echo
}

trap checkpids CHLD

wait

陷阱是你的朋友。在很多系统中都可能出现ERR。您可以捕获EXIT,或在DEBUG上在每个命令之后执行一段代码。

这除了所有的标准信号。

edit

这是一次意外的登录错误的帐户,所以我没有看到示例的请求。

试试这里,用我的普通账户。

在bash脚本中处理异常

我不相信Bash的内置功能可以做到这一点。

你可以在child退出时得到通知:

#!/bin/sh
set -o monitor        # enable script job control
trap 'echo "child died"' CHLD

然而,在信号处理程序中没有明显的方法来获取子进程的退出状态。

获得子状态通常是底层POSIX api中的等待函数族的工作。不幸的是,Bash对此的支持是有限的——您可以等待一个特定的子进程(并获得其退出状态),也可以等待所有子进程,但总是得到0结果。

似乎不可能做到的是waitpid(-1)的等效,它会阻塞直到任何子进程返回。