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