假设您有以下脚本sandbox.sh
。
(这看起来类似于即使设置了 -e,函数内的命令替换也不会在失败时停止脚本,但我相信有点不同)
在“The Line”,我试图将输出作为位置参数传递给func2
to 。func1
#!/bin/bash
set -eu -o pipefail -E
shopt -s inherit_errexit
function func1() {
local arg="${1}"
echo "(func1)This line shouldn't be reached:arg='${arg}': '${?}'" >&2
}
function func2() {
echo "value from func2"
exit 1
}
var="$(func1 "$(func2)")" # The Line
echo "main:This line shouldn't be reached:var='${var}':'${?}'" >&2
我相信这是一种可能的情况,程序员希望通过以简洁的方式重用现有函数来构造数据。
但是,这会产生以下输出。
$ bash sandbox.sh
(func1)This line shouldn't be reached:arg='value from func2': '0'
main:This line shouldn't be reached:var='':'0'
$
我将此行为解释为调用的子 shellfunc2
并未errexit
从 的子 shell 继承行为func1
。因此,func1
将未设置的值作为其参数。
我试图通过修改“The Line”来使其正常工作,例如
# A.
var="$(set -e; func1 "$(set -e; func2)")" # The Line
或者
# B.
var="$(func1 "$(func2 || exit 1)" || exit 1)" # The Line
但还没有运气。事实上,我对 A. 和 B. 都没有纠正这种行为感到困惑。对此的最佳实践是什么?或者我不应该尝试从一开始就嵌套命令替换吗?
答案1
根据到目前为止的答案(似乎已被删除)和我所做的实验,我开始认为这(嵌套命令替换)只能被视为“不好的做法”或充其量“非常麻烦”。
我能做的最好的就是
#!/bin/bash
set -eu -o pipefail -E
shopt -s inherit_errexit
function func1() {
[[ $? == 0 ]]; return 1 # The boilerplate line
local arg="${1}"
echo "(func1)This line shouldn't be reached:arg='${arg}': '${?}'" >&2
}
function func2() {
echo "value from func2"
return 1
}
var="$(func1 "$(func2)")" # The line
echo "main:This line shouldn't be reached:var='${var}':'${?}'" >&2
这会产生预期的输出(空)和非零 (1) 退出代码。诀窍是检查 是否$?
位于0
函数定义的第一行。需要为“嵌套命令替换”中重用的每个函数定义插入此(样板行)。如果您想使用“嵌套命令替换”语法而不感到焦虑,则需要熟悉每次创建新函数时插入样板文件。
“样板线”真的无害吗?显然,是的,我相信。因为如果您set -eu
,无论哪种方式,每当检测到故障时都会立即中止。但我不擅长 bash 编程,可能错过了一些潜在的陷阱。我真的很想知道更多有经验的人的见解。提前致谢。