进程替换为 grep 缺少预期输出

Process substitution into grep missing expected outputs

假设我有一个输出的程序:

abcd
l33t
1234

我将用 printf 'abcd\nl33t\n1234\n' 模拟。我想同时将此输出提供给两个程序。我的想法是使用 tee 的进程替换。假设我想将输出的副本提供给 grep:

printf 'abcd\nl33t\n1234\n' | tee >(grep '[a-z]' >&2) | grep '[0-9]'

我在 Bash 4.1.2 (Linux, CentOS 6.5) 中得到了以下内容,很好:

l33t
1234
abcd
l33t

但是如果进程替换不是重定向到stderr(即没有>&2),像这样:

printf 'abcd\nl33t\n1234\n' | tee >(grep '[a-z]') | grep '[0-9]'

然后我得到:

l33t
1234
l33t

这就像来自进程替换(第一个 grep)的 stdout 被管道(第二个 grep)之后的进程使用。除了第二个 grep 已经在自己读取东西,所以我想它不应该考虑第一个 grep 的东西。除非我错了(我肯定错了)。

我错过了什么?

说明

就命令行而言,process substitution is just a way of making a special filename. (See also the docs。)所以第二个管道实际上看起来像:

printf 'abcd\nl33t\n1234\n' | tee /dev/fd/nn | grep '[0-9]'

其中 nn 是一些文件描述符编号。 printf 的完整输出转到 /dev/fd/nn,也转到 grep '[0-9]'。因此,只打印数值。

至于>()里面的进程,它继承了父进程的stdout。在这种情况下,标准输出在管道内。因此,grep '[a-z]' 的输出就像 tee 的标准输出一样通过管道。因此,整个管道只传递包含数字的行。

当您改为写入 stderr (>&2) 时,您将绕过最后一个管道阶段。因此,stderr 上的 grep '[a-z]' 输出到终端。

修复

要在不使用 stderr 的情况下解决此问题,您可以为您的屏幕使用另一个别名。例如:

printf 'abcd\nl33t\n1234\n' | tee >(grep '[a-z]' >/dev/tty ) | grep '[0-9]'
                                               # ^^^^^^^^^

这给了我输出

l33t
1234
abcd
l33t

测试这个

为了解决这个问题,我 运行 echo >(ps)ps 进程是 bash 进程 运行 管道的子进程。

我也运行

printf 'abcd\nl33t\n1234\n' | tee >(grep '[a-z]')

最后没有 | grep '[0-9]'。在我的系统上,我看到

abcd    <--- the output of the tee
l33t         ditto
1234         ditto
abcd    <--  the output of the grep '[a-z]'
l33t         ditto

所有五行都进入 grep '[0-9]'

tee 之后你有两个流

abcd
l33t
1234

第一个 grep (>(grep '[a-z]' >&2) 过滤掉

abcd
l33t

并将结果打印到 its(!!!) stderr - 它仍然连接到您的终端...

所以,另一个简单的演示:

printf 'abcd\nl33t\n1234\n' | tee >(grep '[a-z]' >&2) | grep '[0-9]'

这会打印

l33t
1234
abcd
l33t

现在添加 wc -l

printf 'abcd\nl33t\n1234\n' | tee >(grep '[a-z]' >&2) | grep '[0-9]' | wc -l 

你会得到

abcd
l33t
       2

你清楚地看到的地方:

abcd
l33t

是第一个 grep 的标准错误,但第二个 grep 的标准输出被重定向到 wc 并打印

2

现在另一个测试:

printf 'abcd\nl33t\n1234\n' | tee >(grep '[a-z]' ) | cat -

输出

abcd
l33t
1234
abcd
l33t

例如来自 grep 的两行和来自 print

的完整输入

计数:

printf 'abcd\nl33t\n1234\n' | tee >(grep '[a-z]' ) | cat - | wc -l

输出

5