即使设置了 -e 和 shopt -s Heritage_errexit,嵌套命令替换也不会在失败时停止脚本

即使设置了 -e 和 shopt -s Heritage_errexit,嵌套命令替换也不会在失败时停止脚本

假设您有以下脚本sandbox.sh

(这看起来类似于即使设置了 -e,函数内的命令替换也不会在失败时停止脚本,但我相信有点不同)

在“The Line”,我试图将输出作为位置参数传递给func2to 。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 编程,可能错过了一些潜在的陷阱。我真的很想知道更多有经验的人的见解。提前致谢。

相关内容