vda*_*vid 5 bash pipeline io-redirection process-substitution
假设我有一个程序输出:
abcd
l33t
1234
Run Code Online (Sandbox Code Playgroud)
我会用它来模拟printf 'abcd\nl33t\n1234\n'.我想同时将这个输出提供给两个程序.我的想法是使用进程替换tee.假设我想将输出的副本提供给grep:
printf 'abcd\nl33t\n1234\n' | tee >(grep '[a-z]' >&2) | grep '[0-9]'
Run Code Online (Sandbox Code Playgroud)
我使用Bash 4.1.2(Linux,CentOS 6.5)得到以下内容,这很好:
l33t
1234
abcd
l33t
Run Code Online (Sandbox Code Playgroud)
但是如果进程替换没有被重定向到stderr(即没有>&2),就像这样:
printf 'abcd\nl33t\n1234\n' | tee >(grep '[a-z]') | grep '[0-9]'
Run Code Online (Sandbox Code Playgroud)
然后我得到:
l33t
1234
l33t
Run Code Online (Sandbox Code Playgroud)
这就像进程替换的stdout(第一个grep)被管道(第二个grep)之后的进程使用.除了第二个grep已经自己读取东西,所以我想它不应该考虑第一个grep中的东西.除非我弄错了(我肯定是).
我错过了什么?
就命令行而言,进程替换只是创建特殊文件名的一种方法。(另请参阅文档。)因此第二个管道实际上看起来像:
printf 'abcd\nl33t\n1234\n' | tee /dev/fd/nn | grep '[0-9]'
Run Code Online (Sandbox Code Playgroud)
其中nn是一些文件描述符编号。的完整输出printf转到/dev/fd/nn,并且也转到grep '[0-9]'。因此,仅打印数值。
至于 内部的进程>(),它继承了其父进程的 stdout。在这种情况下,该标准输出位于管道内部。因此, 的输出grep '[a-z]'就像 的标准输出一样通过管道tee。因此,整个管道仅传递包含数字的行。
当您改为写入 stderr ( >&2) 时,您将绕过最后一个管道阶段。因此,on stderr 的输出grep '[a-z]'将发送到终端。
要在不使用 stderr 的情况下解决此问题,您可以为屏幕使用另一个别名。例如:
printf 'abcd\nl33t\n1234\n' | tee >(grep '[a-z]' >/dev/tty ) | grep '[0-9]'
# ^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)
这给了我输出
l33t
1234
abcd
l33t
Run Code Online (Sandbox Code Playgroud)
为了解决这个问题,我跑了echo >(ps)。该进程是运行管道的进程 ps的子进程。bash
我也跑了
printf 'abcd\nl33t\n1234\n' | tee >(grep '[a-z]')
Run Code Online (Sandbox Code Playgroud)
没有| grep '[0-9]'最后。在我的系统上,我看到
abcd <--- the output of the tee
l33t ditto
1234 ditto
abcd <-- the output of the grep '[a-z]'
l33t ditto
Run Code Online (Sandbox Code Playgroud)
所有五行都进入grep '[0-9]'.