我试图了解使用管道时如何传达退出状态。假设我要which
用来查找不存在的程序:
which lss
echo $?
1
由于which
找不到位置,lss
我的退出状态为1。这很好。但是,当我尝试以下操作:
which lss | echo $?
0
这表明最后执行的命令已正常退出。我能理解的唯一方法是,也许PIPE也会产生退出状态。这是正确的理解方式吗?
我试图了解使用管道时如何传达退出状态。假设我要which
用来查找不存在的程序:
which lss
echo $?
1
由于which
找不到位置,lss
我的退出状态为1。这很好。但是,当我尝试以下操作:
which lss | echo $?
0
这表明最后执行的命令已正常退出。我能理解的唯一方法是,也许PIPE也会产生退出状态。这是正确的理解方式吗?
Answers:
管道的退出状态是管道中最后一条命令的退出状态(除非在pipefail
支持该命令的外壳程序中设置了shell选项,在这种情况下,退出状态将是通过以下命令退出的管道中最后一条命令的退出状态)非零状态)。
无法从管道内部输出管道的退出状态,因为在管道实际完成执行之前,无法知道该值是什么。
管道
which lss | echo $?
这是荒谬的,因为echo
它不读取其标准输入(管道用于在一个命令的输出到下一个命令的输入之间传递数据)。该命令echo
也不会打印管道的退出状态,但是命令的退出状态会在管道之前立即运行。
$ false
$ which hello | echo $?
1
which: hello: Command not found.
$ true
$ which hello | echo $?
0
which: hello: Command not found.
这是一个更好的示例:
$ echo hello | read a
$ echo $?
0
$ echo nonexistent | { read filename && rm $filename; }
rm: nonexistent: No such file or directory
$ echo $?
1
这意味着管道还可以用于if
:
if gzip -dc file.gz | grep -q 'something'; then
echo 'something was found'
fi
管道的退出状态是右侧命令的退出状态。左侧命令的退出状态将被忽略。
(请注意,which lss | echo $?
不会显示此内容。您将运行which lss | true; echo $?
以显示此内容。在中which lss | echo $?
,echo $?
报告在此管道之前的最后一个命令的状态。)
外壳程序以这种方式运行的原因是,在相当普遍的情况下,应忽略左侧的错误。如果在左侧仍在写时右侧退出(或更通常是关闭其标准输入),则左侧会收到SIGPIPE信号。在这种情况下,通常没有任何错误:右侧不关心数据;如果左侧的作用仅仅是产生此数据,则可以停止它。
但是,如果左侧死于SIGPIPE之外的其他原因,或者左侧的工作不只是在标准输出上生成数据,那么左侧的错误就是真正的错误,应该报告。
在纯文本格式中,唯一的解决方案是使用命名管道。
set -e
mkfifo p
command1 >p & pid1=$!
command2 <p
wait $pid1
在ksh,bash和zsh中,如果管道的任何组件以非零状态退出,则可以指示Shell使管道以非零状态退出。您需要设置以下pipefail
选项:
set -o pipefail
shopt -s pipefail
setopt pipefail
或setopt pipe_fail
)在mksh,bash和zsh中,您可以使用变量PIPESTATUS
(bash,mksh)或pipestatus
(zsh)获得管道中每个组件的状态,该变量是一个数组,其中包含最后一个管道中所有命令的状态($?
)。
是的,PIPE产生退出状态;如果要在PIPE之前使用命令的退出代码,则可以使用$PIPESTATUS
根据此Stack Overflow Q&A的说明,要在使用管道时打印命令的退出状态,可以执行以下操作:
your_command | echo $PIPESTATUS
或使用命令设置pipefail set -o pipefail
(要取消设置,请执行set +o pipefail
),然后使用$?
代替$PIPESTATUS
。
your_command | echo $?
这些命令仅在您至少运行一次后才起作用;这意味着PIPESTATUS
仅当您在使用管道的命令之后运行它时才起作用,如下所示:
command_which_exit_10 | command_which_exit_1
echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"
您将获得10的奖励${PIPESTATUS[0]}
和1的奖励${PIPESTATUS[1]}
。有关更多信息,请参见此U&L问答。
PIPESTATUS
在最后一个管道中包含了命令的退出状态,但第一个示例的意义远没有somecmd | echo $?
Kusalananda在其答案中处理过的有意义。false; true | echo $PIPESTATUS
打印1
退出状态为false
。
|exho
确实令人困惑
which lss | echo $?
,退出echo
之前which
已启动;生成命令行的shellecho
无法知道以后的退出状态which
。