如何在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、按顺序等待它们并求和退出状态更好的解决方案呢?


当前回答

我真的很喜欢卢卡的回答,但需要它为zsh,所以这里是作为参考:

pids=()

# run processes and store pids in array
for i in $n_procs; do
    ./procs[${i}] &
    pids+=($!)
done

# wait for all pids
for pid in ${pids[*]}; do
    wait $pid
done```

其他回答

http://jeremy.zawodny.com/blog/archives/010717.html:

#!/bin/bash

FAIL=0

echo "starting"

./sleeper 2 0 &
./sleeper 2 1 &
./sleeper 3 0 &
./sleeper 2 0 &

for job in `jobs -p`
do
echo $job
    wait $job || let "FAIL+=1"
done

echo $FAIL

if [ "$FAIL" == "0" ];
then
echo "YAY!"
else
echo "FAIL! ($FAIL)"
fi

我想运行doCalculations;echo $ ?”>>/tmp/acc在一个子shell中被发送到后台,然后等待,然后/tmp/acc将包含退出状态,每行一个。不过,我不知道多个进程附加到累加器文件的任何后果。

下面是这个建议的一个例子:

文件:doCalcualtions

#!/bin/sh

random -e 20
sleep $?
random -e 10

文件:

#!/bin/sh

rm /tmp/acc

for i in $( seq 0 20 ) 
do
        ( ./doCalculations "$i"; echo "$?" >>/tmp/acc ) &
done

wait

cat /tmp/acc | fmt
rm /tmp/acc

running ./try的输出

5 1 9 6 8 1 2 0 9 6 5 9 6 0 0 4 9 5 5 9 8

如果您有bash 4.2或更高版本可用,以下内容可能对您有用。它使用关联数组存储任务名称及其“代码”,以及任务名称及其pid。我还内置了一个简单的速率限制方法,如果你的任务消耗了大量CPU或I/O时间,你想限制并发任务的数量,这个方法可能会派上用场。

脚本在第一个循环中启动所有任务,在第二个循环中使用结果。

对于简单的情况,这有点过分,但它允许非常简洁的东西。例如,可以将每个任务的错误消息存储在另一个关联数组中,并在一切都解决后打印它们。

#! /bin/bash

main () {
    local -A pids=()
    local -A tasks=([task1]="echo 1"
                    [task2]="echo 2"
                    [task3]="echo 3"
                    [task4]="false"
                    [task5]="echo 5"
                    [task6]="false")
    local max_concurrent_tasks=2

    for key in "${!tasks[@]}"; do
        while [ $(jobs 2>&1 | grep -c Running) -ge "$max_concurrent_tasks" ]; do
            sleep 1 # gnu sleep allows floating point here...
        done
        ${tasks[$key]} &
        pids+=(["$key"]="$!")
    done

    errors=0
    for key in "${!tasks[@]}"; do
        pid=${pids[$key]}
        local cur_ret=0
        if [ -z "$pid" ]; then
            echo "No Job ID known for the $key process" # should never happen
            cur_ret=1
        else
            wait $pid
            cur_ret=$?
        fi
        if [ "$cur_ret" -ne 0 ]; then
            errors=$(($errors + 1))
            echo "$key (${tasks[$key]}) failed."
        fi
    done

    return $errors
}

main

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

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

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

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

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

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

我看到这里列出了很多很好的例子,我也想把我的举出来。

#! /bin/bash

items="1 2 3 4 5 6"
pids=""

for item in $items; do
    sleep $item &
    pids+="$! "
done

for pid in $pids; do
    wait $pid
    if [ $? -eq 0 ]; then
        echo "SUCCESS - Job $pid exited with a status of $?"
    else
        echo "FAILED - Job $pid exited with a status of $?"
    fi
done

我使用非常类似的方法并行启动/停止服务器/服务,并检查每个退出状态。对我来说很好。希望这能帮助到一些人!