我有一个以下脚本sandbox.sh
,
#!/bin/bash
set -eu -o pipefail -E
function func1() {
echo "FUNC1"
exit 1
}
function func2() {
local ret
ret=$(func1)
echo $ret
echo "(func2)This line shouldn't be reached:'${?}'" >&2
}
var=$(func1) # The Line
echo "main:This line shouldn't be reached:'${var}':'${?}'" >&2
(GNU bash,版本 4.4.20(1)-release (x86_64-pc-linux-gnu))
这会按预期停止执行,
$ bash -eu sandbox.sh
$
但是,如果我将“The Line”修改为var=$(func2)
call func1
through func2
,它将给出以下输出
$ bash sandbox.sh
(func2)This line shouldn't be reached:'0'
main:This line shouldn't be reached:'FUNC1':'0'
$
对我来说,当命令替换放置在函数中时,它的行为似乎有所不同,但我不明白为什么 bash 是这样设计的。另外,很可能出现一个函数的输出被另一个函数使用的情况,这种差异令人困惑。
注意:如果我像下面这样重写 func2 ,
function func2() {
func1
}
剧本停在The Line。然而,我相信,程序员经常想要操作 func1 的输出。
答案1
如果我们慢慢来,这一切都是完全可以理解的。需要更多日志记录,因此运行巴什带有-x
参数,它将在 bash 执行命令之前回显命令,前缀为+
.
第一次运行
$ bash -x sandbox.sh; echo $?
+ set -eu -o pipefail -E
++ func1
++ echo FUNC1
++ exit 1
+ var=FUNC1
1
-e
说这命令返回非零时 shell 将立即退出。但至关重要的是,您func1
在子 shell 中运行(使用$(
)
)。上面的跟踪通过使用两个+
s 作为前缀 (++
) 显示了这一事实。- 子 shell 在 stdout 上输出
FUNC1
,然后以返回代码 1 退出。- 注意:
-e
在该子 shell 内关闭。子 shell 退出的原因是由于命令exit
而不是-e
.由于func1
所写的方式,您无法真正说出这一点。
- 注意:
- 回到第一个 shell,我们分配
FUNC1
给变量变量。然而,这个赋值命令的退出代码是最后一个命令替换的退出代码。重击看到此失败(即非零退出代码),然后退出。
引用手册的简单的命令扩展部分:
如果其中一个扩展包含命令替换,则该命令的退出状态是最后执行的命令替换的退出状态。
第二次运行
与第一次运行的解释完全相同。我们再次注意到,它-e
在子 shell 内不起作用。然而这一次,有一个重大的区别——我们对正在发生的事情有了更清晰的认识。
- 的退出代码
func2
是其最后一个命令的退出代码 - 那
echo
总是成功的。 func2
总是成功- 任务总是成功的。
-e
没有影响。
shopt -s inherit_errexit
?
这将在子 shell 中打开-e
。然而,这是一个困难的伙伴。它不保证我们在命令失败时断言。
考虑一下:
set -e
shopt -s inherit_errexit
f() { echo a; (exit 22); echo b; }
echo "f says [$(f)] $?"
echo byee
这次命令替换是 的一部分echo
,而不是赋值的一部分,我们得到
+ set -e
+ shopt -s inherit_errexit
++ f
++ echo a
++ exit 22
+ echo 'f says [a] 22'
f says [a] 22
+ echo byee
byee
- 子 shell 看到一个失败的命令,并显示退出代码 22。由于该命令
-e
有效,因此 shell 会以代码 22 退出(echo b
不执行)。 - 返回第一个 shell,
echo
获取a
的输出f
以及22
子 shell 的退出代码 - 事实是,与赋值不同,退出代码
echo
为零。
版本
$ bash --version
GNU bash, version 5.0.17(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.