在我的 bash 脚本中,我经常使用管道,并且想知道管道的哪个阶段导致出现错误时出现问题。这些片段的基本结构是:
#!/bin/bash
ProduceCommand 2>/dev/null | ConsumeCommand >/dev/null 2>&1
PipeErrors=("${PIPESTATUS[@]}")
[[ "${PipeErrors[0]}" -eq '0' ]] || { HandleErrorInProduceCommand; }
[[ "${PipeErrors[1]}" -eq '0' ]] || { HandleErrorInConsumeCommand; }
tee
现在(第一次很有趣)我处于这样一种情况,如果我可以使用or ,那就太好了pee
。但是$PIPESTATUS
在使用这些命令时会发生什么?例如:
#!/bin/bash
ProduceCommand 2>/dev/null | tee >(ConsumeCommand1) >(ConsumeCommand2) >/dev/null 2>&1
PipeErrors=("${PIPESTATUS[@]}")
或者
#!/bin/bash
ProduceCommand 2>/dev/null | pee ConsumeCommand1 ConsumeCommand2 2>/dev/null
PipeErrors=("${PIPESTATUS[@]}")
我相信在这两种情况下都${PipeErrors[0]}
反映了ProduceCommand
. 此外,假设它分别${PipeErrors[1]}
反映了tee
或pee
本身的错误状态是合乎逻辑的。
但这导致我至少遇到两个理解问题:
tee
or的错误状态(返回值)是pee
什么?我在手册页中没有找到关于这一点的准确陈述。如果其中一个消费命令失败,它们是否会返回硬编码的错误状态,或者它们是否会以某种方式传递消费命令的错误状态(ssh
例如)?如果是前者,我们如何找出哪些消耗命令是罪魁祸首?如果是后者,中继哪个错误状态?仅仅是首先失败的命令吗?AFAIK、bash 或
tee
orpee
命令本身分别在内部使用管道(fifos)来获取ProduceCommand
消耗命令的输出。这意味着我们有一个管道,其(第一个,在这种情况下,唯一的)接收端是一个管道本身。这不应该影响$PipeErrors
上面的示例代码,但我真的不确定。
有人可以对此有所了解吗?
能够将所有数据复制到所有输出文件时为 0,否则为 >0。请参阅规范。GNU coreutils 的实现有额外
tee
的选项可以在写入管道时忽略错误(如那些用于实现的>(...)
):没有选项可以知道哪些输出失败(如果有的话)[1]。
但是您的问题似乎是关于在
>(..)
进程替换中运行的命令的退出状态是否以任何方式反映在 中PIPESTATUS
,或者是否可以以任何方式反映在PIPESTATUS
.答案是否定的。
首先,请注意它
>(...)
更像是... &
后台命令而不是...|...
管道命令。在如下片段中:无法保证
cmd
在您运行时已经完成echo ${PIPESTATUS[@]}
。但它们并不完全一样
...&
,因为你不能wait
为它们获取它们的状态,也不能从中获取它们的状态$!
——除非在一些有限的情况下,它们不包括它们与tee
或其他外部命令的使用:如您所见,
tee
主 shell 都在>(...)
.[1]
pee
运行输出“子命令”本身(并等待它们完成)的命令可能更智能,并在其退出状态中反映其中哪些已失败(例如,将第 1 位设置为第 2 位对于第二个,依此类推,最多 8 个子命令),但也不做这样的事情。你总是可以这样做:
这将处理启动生产者和消费者的每个子shell中的错误。
我们将标准输出复制到 fd 3 上,因此可以为错误处理程序恢复原始标准输出,因为我们不希望它们的输出(如果有的话)通过管道。
如果您希望错误处理程序在主 shell 进程中运行(例如,它可以退出它),您可以让这些子 shell 通过一些命令替换管道将退出状态传递给父 shell:
这样可以避免
$PIPESTATUS
bashism,或者您可以避免>(...)
kshism 并用普通管道替换它们:或者将这两种方法结合起来,然后获得标准
sh
语法,并获得对tee
' 的退出状态的访问作为奖励。请注意,
tee
如果其中一个进程在未读取所有输入的情况下退出,则可能会死于 SIGPIPE,这意味着另一个进程也可能会丢失一些输入。因此,检查其退出状态可能也很重要。正如@UncleBilly 已经指出的那样,通过 GNU 实现
tee
,可以使用该-p
选项(tee
忽略 SIGPIPE 信号并在管道损坏时停止尝试将更多数据写入管道)来解决此问题。对于其他实现,您可以替换
tee ...
为(trap '' PIPE; exec tee ...)
以获得类似的行为(尽管即使tee
没有中止,也可能会出现一些关于损坏管道的错误消息)。