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

phk*_*phk 11 shell posix command-substitution assignment exit-status

请参阅以下示例及其在 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

正如/sf/ask/478414121/ 上投票最高的答案所提到的:

$? 用于查找上次执行命令的返回值。

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

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

所以看起来好像一个赋值本身算作一个命令(或者更确切地说是一个管道部分),它的退出值为零,但它应用赋值的右侧之前(例如,我的示例中的命令替换调用)。

从实际的角度来看,我看到这种行为是有道理的,但对我来说,作业本身按该顺序计数似乎有些不寻常。也许为了让我更清楚为什么这对我来说很奇怪,让我们假设赋值是一个函数:

ASSIGNMENT( VARIABLE, VALUE )
Run Code Online (Sandbox Code Playgroud)

那么foo="bar"将是

ASSIGNMENT( "foo", "bar" )
Run Code Online (Sandbox Code Playgroud)

并且foo=$(false)会像

ASSIGNMENT( "foo", EXECUTE( "false" ) )
Run Code Online (Sandbox Code Playgroud)

这意味着首先EXECUTE运行,然后运行,但它仍然是这里重要的状态。 ASSIGNMENTEXECUTE

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

ica*_*rus 10

任务的退出状态很奇怪。赋值失败最明显的方式是如果目标变量被标记为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 $
Run Code Online (Sandbox Code Playgroud)

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

引用POSIX 标准

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

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

 foo=$(err 42)
Run Code Online (Sandbox Code Playgroud)

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