赋值就像具有退出状态的命令,除非有命令替换?

赋值就像具有退出状态的命令,除非有命令替换?

请参阅以下示例及其在 POSIX shell 中的输出:

  1. false;echo $?或者false || echo 11
  2. false;foo="bar";echo $?或者foo="bar" && echo 00
  3. foo=$(false);echo $?或者foo=$(false) || echo 11
  4. foo=$(true);echo $?或者foo=$(true) && echo 00

正如得票最高的答案所提到的https://stackoverflow.com/questions/6834487/what-is-the-variable-in-shell-scripting

$?用于查找最后执行的命令的返回值。

在这种情况下,这可能有点误导,所以让我们获取 POSIX 定义,该定义也在该线程的帖子中引用:

?扩展到最近管道的十进制退出状态(请参阅管道)。

因此,看起来好像分配本身算作一个命令(或者更确切地说是一个管道部分),其退出值为零,但适用分配的右侧(例如,我的示例中的命令替换调用)。

从实际的角度来看,我明白这种行为是如何有意义的,但对我来说,作业本身按这个顺序计数似乎有点不寻常。也许为了更清楚为什么这对我来说很奇怪,让我们假设分配是一个函数:

ASSIGNMENT( VARIABLE, VALUE )

那么foo="bar"将会是

ASSIGNMENT( "foo", "bar" )

foo=$(false)是这样的

ASSIGNMENT( "foo", EXECUTE( "false" ) )

这意味着EXECUTE运行第一的只是之后 ASSIGNMENT已运行,但这里的状态仍然很EXECUTE重要。

我的评估是正确的还是我误解/遗漏了什么?这些是我认为这种行为“奇怪”的正确理由吗?

答案1

作业的退出状态是奇怪的。赋值失败的最明显的方式是目标变量被标记为readonly

$ err(){ echo error ; return ${1:-1} ; }
$ PS1='$? $ '
0 $ err 42
error
42 $ A=$(err 12)
12 $ if A=$(err 9) ; then echo wrong ; else E=$? ; echo "E=$E ?=$?" ; fi
E=9 ?=0
0 $ readonly A
0 $ if A=$(err 10) ; then echo wrong ; else E=$? ; echo "E=$E ?=$?" ; fi
A: is read only
1 $

请注意,if 语句的 true 和 false 路径均未采用,赋值失败会停止整个语句的执行。如果分配失败,POSIX 模式下的 bash 以及 ksh93 和 zsh 都将中止脚本。

引用POSIX 标准对此

没有命令名称但包含命令替换的命令具有 shell 执行的最后一个命令替换的退出状态。

这正是shell语法中涉及的部分

 foo=$(err 42)

它来自simple_command(simple_command → cmd_prefix → ASSIGNMENT_WORD)。因此,如果分配成功,则退出状态为零,除非涉及命令替换,在这种情况下,退出状态是最后一个的状态。如果分配失败,则退出状态不为零,但您可能无法捕获它。

答案2

你说,

...看起来好像赋值本身算作一个命令...退出值为零,但它适用于赋值的右侧之前(例如,命令替换调用...)

这并不是一个可怕的看待问题的方式。但这有点过于简单化了。总体返回状态来自

A=$(命令1) B=$(命令2) C=$(命令3) D=$(命令4) E=mc 2
是退出状态。赋值后发生的赋值不会将整体退出状态设置为 0。cmd4E=D=

另外,如伊卡洛斯 指出,变量可以设置为只读。考虑伊卡洛斯示例的以下变体:

$ err() { echo "stdout $*"; echo "stderr $*" >&2; return ${1:-1}; }
$ readonly A
$ Z=$(err 41 zebra) A=$(err 42 antelope) B=$(err 43 badger)
stderr 41 zebra
stderr 42 antelope
bash: A: readonly variable
$ echo $?
1
$ printf "%s = %s\n" Z "$Z" A "$A" B "$B"
Z = stdout 41 zebra
A =
B =
$

即使是只读的,bash 也会执行-A右侧的命令替换A=然后中止该命令,因为A它是只读的。这进一步与您的解释相矛盾,即赋值的退出值在赋值的右侧之前应用。

相关内容