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

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

我理解这些:

true;   echo "$?"      # 0
false;  echo "$?"      # 1
true  | echo "$?"      # 0

但不是这个:

false | echo "$?"      # 0

...为什么不打印1

我怎样才能强制管道发生故障,然后再恢复呢1

答案1

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

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

true;   echo "$?"      # 0
false;  echo "$?"      # 1
true  | echo "$?"      # 0

是相同的:

true   
echo "$?"      # 0
false  
echo "$?"      # 1
echo "$?"      # 0

trueecho $?在同时执行之前不会执行。

答案2

在 中false | echo $?$?不是退出状态,false因为$?扩展到小数出口最近的状态管道[1],不是最新的命令,子外壳或者子进程。在 中false | echo $?false不是管道,而只是管道的一部分。一个简单的完全的命令就像false;是一个管道,即使不包含任何|.

假设set -o pipefail打开并且 的退出状态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;变量BASHPID不是在 3 个子进程设置和运行之前展开。像这样的特殊参数$?在这方面并不特殊;它们像任何其他变量一样被扩展。

答案3

这是因为管道的两个部分都是并行运行的,因此当命令已经开始打印时,false命令尚未完成(并设置$?),这仍然是echo$?以前的命令;在你的情况下,很可能是最后echo执行的(并且可能是成功的)。

相关内容