第一次运行

第一次运行

我有一个以下脚本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 func1through 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.

相关内容