【问题标题】:Waiting for multiple processes in bash with set -e使用 set -e 在 bash 中等待多个进程
【发布时间】:2018-09-06 09:00:06
【问题描述】:

我有一个 bash 脚本,我想在其中并行运行两个进程,如果其中一个进程返回非零,则脚本会失败。我最初尝试的一个最小示例是:

#!/bin/bash

set -e
(sleep 3 ; true ) &
(sleep 4 ; false ) &
wait %1 && wait %2
echo "Still here, exit code: $?"

正如预期的那样,这不会打印消息,因为wait %1 && wait %2 失败并且脚本由于set -e 而退出。但是,如果 waits 被反转,使得第一个具有非零状态 (wait %2 && wait %1),则消息打印出来:

$ bash wait_test.sh
Still here, exit code: 1

将每个 wait 放在自己的行上可以按我的意愿工作并在任何一个进程失败时退出脚本,但它不适用于 && 的事实让我怀疑我在这里误解了一些东西.

谁能解释这是怎么回事?

【问题讨论】:

    标签: bash


    【解决方案1】:

    使用 GNU Parallel 及其“故障处理”,您可以非常优雅地实现您想要的。

    一般来说,它会并行运行与 CPU 内核一样多的作业。

    在你的情况下,试试这个,它说 “如果一个或多个作业失败,则以失败状态退出”

    #!/bin/bash
    
    cat <<EOF | parallel --halt soon,fail=1  
    echo Job 1; exit 0
    echo Job 2; exit 1
    EOF
    echo GNU Parallel exit status: $?
    

    样本输出

    Job 1
    Job 2
    parallel: This job failed:
    echo Job 2; exit 1
    GNU Parallel exit status: 1
    

    现在运行它,确保没有作业失败:

    #!/bin/bash
    
    cat <<EOF | parallel --halt soon,fail=1  
    echo Job 1; exit 0
    echo Job 2; exit 0
    EOF
    echo GNU Parallel exit status: $?
    

    样本输出

    Job 1
    Job 2
    GNU Parallel exit status: 0
    

    如果您不喜欢 heredoc 语法,您可以将作业列表放在一个名为 jobs.txt 的文件中,如下所示:

    echo Job 1; exit 0
    echo Job 2; exit 0
    

    然后运行:

    parallel --halt soon,fail=1 < jobs.txt
    

    【讨论】:

    • 谢谢。我注意到很多类似的问题都有建议使用 GNU Parallel 的答案,它确实看起来很棒,但不幸的是,这个脚本需要在 git-bash 下的 Windows 开发人员环境中工作。即使有可能让 Parallel 在 Windows 中工作,我也不能仅仅因为我希望这是一种加速单个脚本中特定瓶颈的简单方法来证明对我们的开发环境进行更改是合理的。
    • 很公平。这只是我认为值得一提的事情。它可能会帮助其他人。祝你的项目好运。
    • @bmerrihort 如果 GNU Parallel 确实在 Windows 上的 git-bash 下工作,请考虑使用 parallel --embed。这样你仍然只在谈论一个脚本。
    【解决方案2】:

    来自bash 关于set 用法的手册部分

    -e 如果管道(可能由单个简单命令组成)、列表或复合命令(参见上面的 SHELL GRAMMAR)以非零状态退出,则立即退出。如果失败的命令是紧跟在 while 或 until 关键字之后的命令列表的一部分、是在 if 或 elif 保留字之后的测试的一部分、在 && 或 || 中执行的任何命令的一部分,则 shell 不会退出列出除了最后一个 && 或 || 之后的命令,管道中除最后一个之外的任何命令,或者命令的返回值是否用 ! 反转。如果子shell 以外的复合命令由于在忽略-e 时命令失败而返回非零状态,则shell 不会退出。如果设置了 ERR 陷阱,则会在 shell 退出之前执行。该选项分别适用于 shell 环境和每个子 shell 环境(参见上面的命令执行环境),并且可能导致子 shell 在执行子 shell 中的所有命令之前退出。

    tl;dr

    在 bash 脚本中,对于这样的命令列表

    command1 && command2
    

    command1在单独的环境中运行,因此不会影响脚本的执行环境。但是command2是在当前环境下运行的,所以会影响

    【讨论】:

    • 有趣 - 我没想到 &amp;&amp; 列表中的命令之一失败会导致脚本退出,但假设如果整个列表评估为非零那么它会。一个更简单的例子表明这是错误的:false &amp;&amp; true 没有退出,这不是我对该文档的期望。将它放在子shell (false &amp;&amp; true) 中的行为符合预期,我猜是因为整个命令不再算作 &amp;&amp; 列表的一部分。
    猜你喜欢
    • 1970-01-01
    • 2021-10-09
    • 2013-10-20
    • 2015-02-25
    • 2014-07-02
    • 2015-05-08
    • 1970-01-01
    • 2016-06-18
    • 1970-01-01
    相关资源
    最近更新 更多