管道“假”不会给出非零结果代码

lon*_*nix 7 bash pipe exit-status

我明白这些:

true;   echo "$?"      # 0
false;  echo "$?"      # 1
true  | echo "$?"      # 0
Run Code Online (Sandbox Code Playgroud)

但不是这个:

false | echo "$?"      # 0
Run Code Online (Sandbox Code Playgroud)

...为什么不打印1

我怎么能在管道中强制失败,然后再得到1呢?

Phi*_*ing 15

两者的结果true | echo "$?"false | echo "$?"误导。的内容"$?"将命令通过管道false传输到命令之前设置echo

为了执行这些行,bash 设置了一个命令管道。管道已设置,然后命令并行启动。所以在你的例子中:

true;   echo "$?"      # 0
false;  echo "$?"      # 1
true  | echo "$?"      # 0
Run Code Online (Sandbox Code Playgroud)

是相同的:

true   
echo "$?"      # 0
false  
echo "$?"      # 1
echo "$?"      # 0
Run Code Online (Sandbox Code Playgroud)

true在同时执行之前不执行echo $?

  • @mosvy 在这一点上我错了,bash 在调用设置管道所需的 `fork()` 后需要执行 shell 扩展。这会令人惊讶,但实际上绝对没有任何后果。`$?` 的值无法在您描述的窗口中更改,因此这确实没有实际意义。逻辑顺序是使用分叉之前的值。也就是说,即使在 CPU 上实际时间不同,bash 的逻辑也会像我的陈述正确一样工作。 (7认同)
  • (@mosvy) 不。`false | echo "$?"` 在 _subshel​​l_ 中运行 `false`,并运行 `echo "$?"` 包括在另一个子 shell 或父 shell 中扩展 `$?`(对于 bash,请参阅手册中的 shopt lastpipe 或此处的许多 Q )。在任何一种情况下,用于`$?` 的状态值(确定性地)不是在第一个子shell 中设置的值,因为在该子shell 中设置的值不会影响另一个子shell 或父级(尽管父bash 随后会设置状态数组“PIPESTATUS”中的子shell)。类似地`cd /elsewhere | echo "$PWD"` 不会输出 `/elsewhere`。 (4认同)
  • “`$?` 的内容将在将命令 false 传递到命令 echo 之前扩展” 错误。它可能发生在之前或之后,取决于内核决定调度管道两侧的顺序。 (2认同)
  • @mosvy 与我之前的声明内联,`$?` 的值是在fork 之前设置的,并且子shell 不能修改彼此的变量。因此,扩展 `$?` 的实际时间永远不会改变它的值。这直接意味着你的陈述“*这个答案给出了错误的解释,应该删除*”太强了,因为你在争论一个[moot point](https://www.dictionary.com/browse/moot--point )。我的措辞是指[逻辑时间](https://en.wikipedia.org/wiki/Vector_clock)而不是时间顺序。 (2认同)

mos*_*svy 7

false | echo $?,$?不是false因为$?扩展到最近管道[1]的十进制退出状态,而不是最近的命令子shell子进程的退出状态。在false | echo $?,false不是管道,而只是管道的一部分。一个简单的完整命令就像false;一个管道,即使不包含任何|.

假设set -o pipefail是 on 并且 的退出状态false | echo $?将是false$?也不能退出当前管道的状态,因为当前管道在它回应它时还没有退出。

管道两侧(始终并行运行)的启动或终止顺序无关,或者echo $?实际上是在子进程还是子shell 中运行都无关紧要。

FWIW,当在子shell中时,变量和其他参数总是在当前子shell的上下文中扩展:如果false | echo $?是通过为管道的两侧分叉单独的进程来实现的(在bash中是这种情况,但不是在所有shell中),$?将在子进程被扩展,之后fork()管道的左侧已退出[2],和之后可能。

我怎么能在管道中强制失败,然后得到 1?

您使用set -o pipefail,它在 bash、zsh 和 ksh 中受支持,并且应该包含在标准的未来版本中。但如上所述,这只影响管道退出$? 的值,而不影响管道本身。


[1] 2.5.2 SUSv4 标准中的特殊参数。在这种情况下,命令替换是否被视为管道取决于 shell;在大多数历史和现在的 shell 中:; echo `exit 13` $?都会打印13,但在某些(如 dash、yash 或 pdksh 派生的)中它会打印0.

[2] 一个可能有助于理解的简单 bash 示例是echo $BASHPID >&2 | echo $BASHPID >&2 | echo $BASHPID >&2;在设置和运行 3 个子进程之前,不会扩展该BASHPID变量。特殊参数 like在这方面并不特殊;它们像任何其他变量一样被扩展。$?