当前位置:  开发笔记 > 编程语言 > 正文

如何在bash命令替换中使用`set -e`?

如何解决《如何在bash命令替换中使用`set-e`?》经验,为你挑选了1个好方法。

我有一个简单的shell脚本,其中包含以下内容:

#!/usr/bin/env bash
set -eu
set -o pipefail

我还具有以下功能:

foo() {
  printf "Foo working... "
  echo "Failed!"
  false  # point of interest #1
  true   # point of interest #2
}

执行foo()作为常规命令按预期工作:脚本退出时#1,由于返回码false是非零和我们使用set -e

我的目标是将函数的输出捕获到foo()变量中,并仅在执行时出现错误时打印它foo()。这是我想出的:

printf "Doing something that could fail... "
if a="$(foo 2>&1)"; then
  echo "Success!"
else
  code=$?
  echo "Error:"
  printf "${a}"
  exit $code
fi

该脚本不会在处退出,#1并且会执行"Success!"if语句的路径。注释掉trueat将#2导致执行语句的"Error:"路径if

看来bash只是忽略set -e了替换内部,而该if语句只是检查中最后一条命令的返回码foo()

问:什么原因导致这种奇怪的行为?

答:这就是bash的工作方式,这是正常现象

问:有什么方法可以使bash set -e在命令替换中得到尊重并使其正常工作?

答:您不应set -e为此目的使用

问:如果不set -e使用该功能,您将如何实现(即,仅当执行该功能时出现问题时才输出该功能的输出)?

答:请参阅已接受的答案和我的“最终决定”部分。

我在用:

GNU bash版本5.0.11(1)-发行版(x86_64-apple-darwin18.6.0)

最终想法/总结(可能对其他人有用):

Beware that using if ...; then, or even && ... || ... will disable most kinds of "traditional" bash error handling methods (this includes set -e and trap ... ERR + set -o errtrace) by design. If you want to do something like I did, you probably should check the return codes inside your function manually and return a non-null exit code by hand (dangerous_command || return 1) to avoid continuing execution on errors (you can do this whether you use set -e or not).

As answered, set -e does not propagate inside command substitutions by design. If you wish to implement error handling logic which does, you can use trap ... ERR in combination with set -o errtrace, which will work with functions running inside command substitutions (that is unless you put them inside an if statement, which will disable trap ... ERR as well, so in this case manual return code checking is your only option if you wish to stop your function on errors).

If you think about it, this whole behaviour kind of makes sense: you wouldn't expect your script to terminate on a command "guarded" by an if statement, as the whole point of your if statement is checking whether the command succeeds or not.

Personally I still wouldn't go as far as avoiding set -e and trap ... ERR entirely as they can be really useful, but understanding how they behave in different circumstances is important, because they are no silver bullet either.



1> anubhava..:

问:如果不set -e使用该功能,您将如何实现(即,仅当执行该功能时出现问题时才输出该功能的输出)?

您可以通过检查函数的返回值来使用这种方式:

#!/usr/bin/env bash

foo() {
  local n=$RANDOM
  echo "Foo working with random=$n ..."
  (($n % 2))
}

echo "Doing something that could fail..."
a="$(foo 2>&1)"
code=$?
if (($code == 0)); then
  echo "Success!"
else
  printf '{"ErrorCode": %d, "ErrorMessage": "%s"}\n' $code "$a"
  exit $code
fi

现在将其运行为:

$> ./errScript.sh
Doing something that could fail...
Success!
$> ./errScript.sh
Doing something that could fail...
{"ErrorCode": 1, "ErrorMessage": "Foo working with random=27662 ..."}
$> ./errScript.sh
Doing something that could fail...
Success!
$> ./errScript.sh
Doing something that could fail...
{"ErrorCode": 1, "ErrorMessage": "Foo working with random=31864 ..."}

如果$RANDOM为偶数,则该伪函数代码返回失败,如果为$RANDOM奇数,则返回成功。


原始问题的原始答案

您还需要启用set -e命令替换:

#!/usr/bin/env bash
set -eu
set -o pipefail

foo() {
  printf "Foo working... "
  echo "Failed!"
  false  # point of interest #1
  true   # point of interest #2
}

printf "Doing something that could fail... "
a="$(set -e; foo)"
code=$?
if (($code == 0)); then
  echo "Success!"
else
  echo "Error:"
  printf "${a}"
  exit $code
fi

然后将其用作:

./errScript.sh; echo $?

Doing something that could fail... 1

但是请注意,set -e在shell脚本中使用不是理想的选择,并且在许多情况下可能无法退出脚本。

请检查以下重要帖子 set -e

推荐阅读
赛亚兔备_393
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有