【问题标题】:How to make errexit behaviour work in bash functions如何使 errexit 行为在 bash 函数中起作用
【发布时间】:2022-02-04 14:09:38
【问题描述】:

如何让-e / errexit 在 bash 函数中工作,以便函数中的第一个失败命令* 导致函数返回错误代码(就像 -e 在顶层工作一样)。

* 不是布尔表达式的一部分,if/elif/while/etc 等

我运行了以下测试脚本,并且我希望任何名称中包含 f 的函数(即其源代码中的 false 行)返回错误代码,但如果后面有另一个命令,它们不会返回.即使我将函数体放入再次指定set -e 的子shell 中,它也只是在命令失败后盲目地通过函数而不是使用非零状态码退出子shell/函数。

测试的环境/解释器:

我在所有这些 envs/shell 中都得到了相同的结果,我猜这是可以预料的,除非有一个错误。

拱门:

  • bash test.sh (bash 5.1)
  • zsh test.sh (zsh 5.8)
  • sh test.sh(只是 bash 的符号链接)

阿尔卑斯山:

  • docker run -v /test.sh:/test.sh alpine:latest sh /test.sh (BusyBox 1.34)

Ubuntu:

  • docker run -v /test.sh:/test.sh ubuntu:21.04 sh /test.sh
  • docker run -v /test.sh:/test.sh ubuntu:21.04 bash /test.sh (bash 5.1)
  • docker run -v /test.sh:/test.sh ubuntu:21.04 dash /test.sh (bash 5.1)

测试脚本

set -e

t() {
    true
}

f() {
    false
}

tf() {
    true
    false
}

ft() {
    false
    true
}

et() {
    set -e
    true
}

ef() {
    set -e
    false
}

etf() {
    set -e
    true
    false
}

eft() {
    set -e
    false
    true
}

st() {( set -e
    true
)}

sf() {( set -e
    false
)}

stf() {( set -e
    true
    false
)}

sft() {( set -e
    false
    true
)}

for test in t f tf ft _ et ef etf eft _ st sf stf sft; do
    if [ "$test" = '_' ]; then
        echo ""
    elif "$test"; then
        echo "$test: pass"
    else
        echo "$test: fail"
    fi
done

在我的机器上输出

t: pass
f: fail
tf: fail
ft: pass

et: pass
ef: fail
etf: fail
eft: pass

st: pass
sf: fail
stf: fail
sft: pass

期望的输出

没有显着更改函数本身的源代码,即不向函数的每一行添加 if/then 或 || return

t: pass
f: fail
tf: fail
ft: fail

et: pass
ef: fail
etf: fail
eft: fail

st: pass
sf: fail
stf: fail
sft: fail

或者至少在一组中通过/失败/失败/失败,所以我可以使用这种方法来编写健壮的函数。

采用公认的解决方案

使用公认的解决方案,前四个测试给出了预期的结果。其他两组没有,但无关紧要,因为这些是我自己解决这个问题的尝试。根本原因是“当 if/while 条件失败时不要退出脚本”行为会传播到条件中调用的任何函数中,而不仅仅是应用于最终结果。

【问题讨论】:

  • 你没有。封装要求如果ft,例如,如果它的任何命令失败,它就会被写成这样。它的行为方式不取决于调用者。
  • 所以本质上,如果其中的命令失败,除了在每个语句上添加 if/else 或 ||return 之外,没有办法让 bash 函数失败(而不是盲目地向前推进)?跨度>
  • 您可以在函数内部本地设置这些标志,至少对于 /bin/sh 有效。
  • @UlrichEckhardt 我用更多的 shell 添加了更多的测试,包括shzsh,以及几个发行版。正如预期的那样,所有情况下的结果都相同。

标签: bash


【解决方案1】:

如何使错误退出行为在 bash 函数中起作用

调用不在if内部的函数。

我希望任何名称中包含 f 的函数(即其源代码中的假行)返回错误代码

Weeeeeell,您的期望与现实不符。这不是它的实施方式。而标志err<b>exit</b> 将退出您的脚本,而不是返回错误代码。

期望的输出

你可以:

  • 下载 Bash 或其他 shell 源或编写您自己的并实现您想要的行为。

    • 考虑使用 shopt -s errexit_but_return_nonzero_in_if_contexts 或更短的名称来扩展行为
  • 在单独的 shell 中运行它。

    elif bash -ec "$(declare -f "$test"); $test"; then
    
  • 当 Bash 进入应该忽略 set -e 的上下文时,编写一个自定义可加载以“清除”CMD_IGNORE_RETURN 标志。我认为static COMMAND *currently_executing_command; 需要是外部的,然后只是currently_executing_command.flags &amp;= ~CMD_IGNORE_RETURN;

  • 您可以在其中临时禁用 errexit 并获取返回状态的包装器,但您必须在 if 之外调用它:

errreturn() {
    declare -g ERRRETURN
    local flags=$(set +o)
    set +e
    (
        set -e
        "$@"
    )
    ERRRETURN=$?
    eval "$flags"
}

....
        echo ""
    else
        errreturn "$test"
        if (( ERRRETURN == 0 )); then
            echo "$test: pass"
        else
            echo "$test: fail"
        fi
    fi

【讨论】:

  • 谢谢,将函数调用移出if(加上±e 的包装以获取状态代码)让一切正常。我没有意识到 if/elif/while 中命令的“不要因错误而放弃”方面会传播到函数中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-03
  • 2020-04-04
  • 2015-05-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多