gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-29 09:04:01 24 4
gpt4 key购买 nike

我有一个带有以下序言的简单 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 处退出,并且会执行 if 语句的 “Success!” 路径。注释掉 #2 处的 true 会导致执行 if 语句的 "Error:" 路径。

似乎 bash 只是忽略了替换中的 set -eif 语句只是检查 foo() 中最后一个命令的返回码

问: 是什么导致了这种奇怪的行为?

答:这就是 bash 的工作方式,这是正常行为

问: 有没有办法让 bash 在命令替换中尊重 set -e 并使其正常工作?

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

问: 如果不使用 set -e(即仅在执行函数时出错时才打印函数的输出),您将如何实现它?

A:请参阅已接受的答案和我的“最终想法”部分。

我正在使用:

GNU bash, version 5.0.11(1)-release (x86_64-apple-darwin18.6.0)

最后的想法/要点(可能对其他人有用):

注意使用 if ...;然后,甚至&& ... || ... 将禁用大多数类型的“传统”bash 错误处理方法(这包括 set -etrap ... ERR + set -o errtrace) 设计。如果你想像我一样做一些事情,你可能应该手动检查函数内的返回码并手动返回一个非空退出代码(dangerous_command || return 1)以避免继续执行错误(无论是否使用 set -e 都可以这样做)。

正如回答的那样,set -e 按设计 不会在命令替换内部传播。如果您希望实现错误处理逻辑,您可以将 trap ... ERRset -o errtrace 结合使用,这将与在命令替换内运行的函数一起使用(也就是说,除非您将它们放在 if 语句中,这也会禁用 trap ... ERR,因此在这种情况下,手动返回代码检查是您​​唯一的选择,如果你希望在出错时停止你的函数)。

如果你仔细想想,这整个行为是有道理的:你不会期望你的脚本在 if 语句“保护”的命令上终止,因为你的整个点if 语句检查命令是否成功。

就我个人而言,我仍然不会完全避免 set -etrap ... ERR,因为它们确实很有用,但理解 他们在不同情况下的表现很重要,因为他们也不是 Elixir 。

最佳答案

Q: How would you go about implementing this without set -e (i.e. print the output of a function only if something went wrong while executing it)?

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

#!/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

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

Do check this important post on set -e

关于bash - 如何在 bash 命令替换中使用 `set -e`?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58859554/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com