Bash 将 stdout 发送到一个进程,将 stderr 发送到另一个进程

Bash Send stdout to one process and stderr to another process

我对进程替换和 bash 重定向有疑问。

考虑

$ zzz > >(echo fine) 2> >(echo error)

我期望输出 fine error,因为 zzz 不是有效命令,但我只是得到 fine。这是为什么?

这按预期工作

$ zzz 2> >(echo error)
error

如果我调换顺序

$ zzz 2> >(echo error) >(echo fine)
fine
error

这是因为 bash 将输出重定向(> >(echo fine) 部分)应用于 echo error 进程以及 zzz 命令本身。由于 echo fine 不对该输入做任何事情,“错误”文本就消失了。您可以通过将 echo fine 替换为实际显示输入内容的内容来看到这一点:

$ zzz > >(sed 's/^/processed through sed: /') 2> >(echo error)
$ processed through sed: error

请注意(至少在我尝试时),这看起来很奇怪,因为 shell 在 sed 开始输出修改后的错误消息之前打印了它的下一个提示符(“$”)。 >( ) 创建的子进程实际上是后台运行的,因此 shell 不会等待它们完成。

如果您想避免这种情况(或至少解决它),您可以将 stdout 文件描述符复制到不同的 FD,然后将其他进程的输出重定向到该备用 FD。这是使用 FD #3 的示例:

$ zzz 3>&1 > >(echo fine) 2> >(echo error >&3)
fine
error

顺便说一句,如果您以其他顺序放置重定向,则重定向到 echo fine 逻辑上会在 echo error 进程启动后发生,因此 echo fine 的输出会转到终端正常。另一方面,如果你这样做,那么来自 echo fine 命令的错误将被重定向到 echo error.

$ zzz 2> >(echo error) > >(echo fine; yyy)
error
fine

(在此示例中,xxxyyy 的“未找到命令”错误都发送到 echo error,后者将忽略它们。)

BTW^2,zsh 也实现了进程替换,但不以这种方式应用继承的重定向:

zsh% zzz > >(echo fine) 2> >(echo error)
fine
error

据我所知,流程替换没有任何标准定义,所以我认为没有办法定义哪个 shell 的实现“更正确”。

如上所说

$ > >(echo run) 2> >(echo zzz)
run

不输出zzz

但是我们可以通过

来规避这个问题
$ 2> >(echo zzz) > >(echo run)
zzz
run