第一次出错时使bash wait命令失败

第一次出错时使bash wait命令失败,bash,Bash,我有一个bash脚本,可以并行化一些耗时的命令,到目前为止,它运行得非常好。我正在使用wait命令,如下所示: docker pull */* & docker pull */* & docker pull */* & docker pull */* & docker pull */* & docker pull */* & docker pull */* & composer install -n & wait 现在我希望这个脚

我有一个bash脚本,可以并行化一些耗时的命令,到目前为止,它运行得非常好。我正在使用wait命令,如下所示:

docker pull */* &
docker pull */* &
docker pull */* &
docker pull */* &
docker pull */* &
docker pull */* &
docker pull */* &
composer install -n &
wait
现在我希望这个脚本中止所有命令,并在其中一个命令失败时给出退出代码。如何做到这一点


注:*/*是docker映像名称,对上下文不重要

如果命令的返回值为0,则表示成功,其他表示错误。因此,您可以创建一个函数,并在每个命令之前调用它。 (仅当您删除&时,此操作才有效)

另一种选择是

set -e
在脚本的开头

这将导致shell在执行简单命令时立即退出 使用非零退出值退出


额外说明: 如果您有自己的Docker注册表,则不需要并行提取图像。Docker Registry 2.0与Docker 1.7.0及更高版本配合使用,它并行下载图像层,这使过程更快,因此您不必同时提取所有图像


这需要bash来等待-n

这里的技巧是保留生成的子流程的列表,然后分别等待它们(按照它们完成的顺序)。然后,您可以检查完成的流程的返回代码,如果失败,则杀死其中的一部分。例如:

#!/bin/bash

# Remember the pid after each command, keep a list of them.
# pidlist=foo could also go on a line of its own, but I
# find this more readable because I like tabular layouts.
sleep 10 & pidlist="$!"
sleep 10 & pidlist="$pidlist $!"
sleep 10 & pidlist="$pidlist $!"
sleep 10 & pidlist="$pidlist $!"
sleep 10 & pidlist="$pidlist $!"
sleep 10 & pidlist="$pidlist $!"
false    & pidlist="$pidlist $!"

echo $pidlist

# $pidlist intentionally unquoted so every pid in it expands to a
# parameter of its own. Note that $i doesn't have to be the PID of
# the process that finished, it's just important that the loop runs
# as many times as there are PIDs.
for i in $pidlist; do
    # Wait for a single process in the pid list to finish,
    # check if it failed,
    if ! wait -n $pidlist; then
        # If it did, kill the lot. pipe kill's stderr
        # to /dev/null so it doesn't complain about
        # already-finished processes not existing
        # anymore.
        kill $pidlist 2>/dev/null

        # then exit with a non-zero status.
        exit 1
    fi
done

只是一个旁注:您是否尝试测量脚本的时间以及未并行化的任务的时间?并行并不总是更快,尤其是在硬盘访问方面…这个docker pull从docker hub中拉取大文件,如果我们并行化它,可以显著减少执行时间。您可以看到:不起作用<代码>有效在子shell中执行,
退出1
只离开该子shell。将
docker pull
命令替换为
valid sleep 10
以查看最后是否需要
wait
,并在其中添加
valid false
,以查看一旦获得
wait
,则
valid
对您没有帮助。好吧,
set-e
是一个想法,但不幸的是,生成实际上并没有失败,因此这仍然会运行所有命令,并且只有在收集所有子进程后返回带有错误代码的
wait
时才会退出。或者在后台shell生成后退出1,并且它永远不会触发,或者,它将位于背景壳内部,只保留该背景壳,因此它不会对所有其他背景壳产生任何影响。我看不出它是如何工作的。两个注释:
wait-n
需要
bash
4.3或更高版本,并且您可以使用
jobs-p
而不是手动维护
pidlist
。@chepner啊,谢谢。我会记下版本要求。不过,我不认为
jobs-p
是个好主意。如果子流程带着错误退出,它可能会很快退出—可能在运行
jobs-p
之前。这意味着,根据竞争条件,出错进程可能不会出现在要监视的进程列表中,并且它们的退出状态可能不会被检查。为什么这样一个基本命令的工作如此糟糕?我正在尝试制作一个简单的构建脚本,步骤a,B,然后C——它们应该同时运行,整个过程应该在第一次失败时崩溃。相反,现在我有了一个npm构建例程,它永远不会失败,现在我必须用Gulp或其他方式重写它,因为上面的例子在我想要创建一个external build.sh脚本时起作用,但这绝对是一个糟糕的解决方案。我期待类似于
wait--all
(我会调用当前行为
wait--any
)。。。我不敢相信这里的复杂性。
#!/bin/bash

# Remember the pid after each command, keep a list of them.
# pidlist=foo could also go on a line of its own, but I
# find this more readable because I like tabular layouts.
sleep 10 & pidlist="$!"
sleep 10 & pidlist="$pidlist $!"
sleep 10 & pidlist="$pidlist $!"
sleep 10 & pidlist="$pidlist $!"
sleep 10 & pidlist="$pidlist $!"
sleep 10 & pidlist="$pidlist $!"
false    & pidlist="$pidlist $!"

echo $pidlist

# $pidlist intentionally unquoted so every pid in it expands to a
# parameter of its own. Note that $i doesn't have to be the PID of
# the process that finished, it's just important that the loop runs
# as many times as there are PIDs.
for i in $pidlist; do
    # Wait for a single process in the pid list to finish,
    # check if it failed,
    if ! wait -n $pidlist; then
        # If it did, kill the lot. pipe kill's stderr
        # to /dev/null so it doesn't complain about
        # already-finished processes not existing
        # anymore.
        kill $pidlist 2>/dev/null

        # then exit with a non-zero status.
        exit 1
    fi
done