进程替换为 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
假设我有一个输出的程序:
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