【问题标题】:bash wait exit on error codebash 等待退出错误代码
【发布时间】:2018-09-05 21:20:16
【问题描述】:

让我们以此为起点:

#!/bin/bash
set -e

echo "Sleeping..."
sleep 2 &

wait

echo "Done"
exit 0

如果后台进程因错误退出,我希望wait 退出整个脚本。引入这样的错误:

#!/bin/bash
set -e

echo "Sleeping..."
sleep SOMETHING_STRANGE_AND_WRONG &

wait

echo "Done"
exit 0

会回显“完成”。我期待wait 退出脚本,因为set -e

我知道可以保存sleeppid,这样查看后台进程的返回值:

#!/bin/bash
set -e

echo "Sleeping..."
sleep SOMETHING_STRANGE_AND_WRONG &
pid=$!

if wait $pid; then
    echo "Success"
else
    echo "Failure!"
    exit 1
fi

echo "Done"
exit 0

但是,当我的脚本中有多个这样的“同步点”时,这会变得很麻烦,并且每个点都有多个子进程要等待。

我对错误代码本身不是很感兴趣,只是它们没有成功。

如果wait 等待的任何子进程未成功,是否有更简洁的方法使wait 失败并退出(因为set -e)?


编辑:我正在寻找wait 失败并在任何子进程失败时退出的解决方案:

#!/bin/bash
set -e

echo "Sleeping..."
sleep SOMETHING_STRANGE_AND_WRONG &
sleep 2 &

wait

echo "Done"
exit 0

我目前以这种方式解决(我觉得很麻烦):

#!/bin/bash
set -e

echo "Sleeping..."
pids=""
sleep SOMETHING_STRANGE_AND_WRONG &
pids+=" $!"
sleep 2 &
pids+=" $!"

for p in $pids; do
    if wait $p; then
        echo "Success"
    else
        echo "Failure"
        exit 1
    fi
done

echo "Done"
exit 0

【问题讨论】:

  • “我期待 [foo] 退出脚本,因为 set -e”的一长串例外是建议您不要使用@的原因987654336@.
  • 请参阅BashFAQ #105 了解更多信息,如果赶时间,请跳过以下练习的寓言。

标签: bash wait


【解决方案1】:

会更短

wait || exit $?

或者如果需要消息,如果失败的进程尚未记录

wait || { echo "background failed: $?" >&2; exit 1;}

也可以用函数代替

exit_fail() {
    echo "$1" >&2
    exit 1
}

...
wait || exit_fail "background failed: $?"

【讨论】:

  • 这似乎无法扩展到多个子流程,例如如果我开始sleep SOMETHNG_STRANGE_AND_WRONG &,然后是sleep 2 &,然后等待wait || exit $?,脚本仍然输出“完成”
  • wait 我的意思是wait $pid
  • || exit 1 语义正是 set -e 所做的,但明确地说,如果命令退出状态为 0,则脚本继续。与 set -e 相反,它是隐式的,如果脚本应该继续在一个失败的命令之后可以使用|| true
  • 确实来自 bash 手册 wait :否则,返回状态是等待的最后一个进程或作业的退出状态。 所以当有多个进程时,必须检查每个进程
  • 但是如果发生故障,等待所有子进程可能是安全的
【解决方案2】:

我在一个函数中手动实现了该功能。

# Wait for subprocesses with pids passed as arguments, exit if any failed.
# $1 info_string: to show the command that the failure/success relates to.
# $* list of pids to wait for and check.
wait_and_check () {
    info_string=$1
    shift
    for p in $*; do
        if wait $p; then
            echo "$info_string process $p success"
        else
            echo "$info_string process $p Failure!"
            exit 1
        fi
    done
}

用法:

pids=""
for p in $bunch_of_stuff ; do
    stuff_function $p &
    pids+=" $!"
done

wait_and_check "stuff" $pids

感觉很多人应该需要类似的东西,所以我很惊讶没有现成的解决方案。

一个缺点是很难检查进程的错误代码。

【讨论】:

  • 当您需要列表时,请考虑使用适当的 bash 数组而不是包含空格的字符串。用pids=( )初始化;附加pids+=( "$1" );用"${pids[@]}"扩展;并使用"$@" 而不是$*。当前代码是不必要的脆弱——如果由于某种原因您在其他地方的某个函数设置了IFS=0,则1014 的pid 将被分成两个条目,114 在未引用的扩展中;如果您以某种方式有一个可以解析为 glob 的值,则扩展会将其替换为匹配的文件名列表。
  • 就问题中要求的wait 具有set -e 行为而言,在所有分支中使用if/then/else/fi 块和echos 似乎也与所需的简洁性相反,当wait || exit 会做的时候。
  • ...顺便说一句,如果你想等待只有下一个命令退出并收集它的退出状态,在现代 bash 中有一个可用的标志:@ 987654337@.
  • 请注意,退出而不杀死所有子进程将导致竞争条件,因为在新运行旧进程之后,新进程可能会运行相同的数据。
  • @NahuelFouilleul 感谢您的提醒,我在陷阱中等待。也许我应该杀了以防万一出现问题,但我必须在某个时候说“足够好”。
猜你喜欢
  • 1970-01-01
  • 2017-08-28
  • 2017-01-08
  • 1970-01-01
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
  • 2012-12-18
  • 2013-09-28
相关资源
最近更新 更多