如何在Unix(或者Windows)中使用(最好是未命名的)管道将一个进程的标准输出发送到多个进程?
时间:2020-03-05 18:52:51 来源:igfitidea点击:
我想将进程proc1的标准输出重定向到两个进程proc2和proc3:
proc2 -> stdout / proc1 \ proc3 -> stdout
我试过了
proc1 | (proc2 & proc3)
但它似乎不起作用,即
echo 123 | (tr 1 a & tr 1 b)
写
b23
而不是stdout
a23 b23
解决方案
回答
编者注:
是一种过程替代,它是某些POSIX兼容外壳的非标准外壳功能:
bash
,ksh
,zsh
。
如所写,答案也意外地通过管道发送了输出过程替换的输出。
回声123 | tee>(tr 1 a)>(tr 1 b)> / dev / null可以防止这种情况,但是有一个陷阱:进程替换的输出将被不可预测地交错,并且,除了在
zsh中,管道可能会终止之前
>()`中的命令可以执行。
在Unix中(或者在Mac上),使用tee
命令:
$ echo 123 | tee >(tr 1 a) | tr 1 b b23 a23
通常,我们将使用tee
将输出重定向到多个文件,但是使用>(...)我们可以
重定向到另一个进程。所以总的来说
$ proc1 | tee >(proc2) ... >(procN-1) | procN
会做你想要的。
在Windows下,我不认为内置外壳具有等效功能。微软的Windows PowerShell有一个tee
命令。
回答
自@dF:提到PowerShell已经开球以来,我想我将展示一种在PowerShell中执行此操作的方法。
PS > "123" | % { $_.Replace( "1", "a"), $_.Replace( "2", "b" ) } a23 1b3
请注意,在创建下一个对象之前,将处理从第一个命令发出的每个对象。这可以允许缩放到非常大的输入。
回答
就像dF所说的一样,bash允许使用运行命令的>>()构造代替文件名。 (也有<()
构造来代替另一个命令的输出来代替文件名,但这已经不重要了,我只为完整性而提到它)。
如果我们没有bash或者在具有较旧版本bash的系统上运行,则可以通过使用FIFO文件来手动执行bash的操作。
实现所需目标的通用方法是:
- 确定应接收命令输出的进程数,并创建尽可能多的FIFO,最好在全局临时文件夹中:
subprocesses="a b c d" mypid=$$ for i in $subprocesses # this way we are compatible with all sh-derived shells do mkfifo /tmp/pipe.$mypid.$i done
- 启动所有子进程,等待来自FIFO的输入:
for i in $subprocesses do tr 1 $i </tmp/pipe.$mypid.$i & # background! done
- 执行准备进入FIFO的命令:
proc1 | tee $(for i in $subprocesses; do echo /tmp/pipe.$mypid.$i; done)
- 最后,删除FIFO:
for i in $subprocesses; do rm /tmp/pipe.$mypid.$i; done
注意:出于兼容性原因,我会在反引号中使用$()
,但我无法编写此答案(反引号用于SO中)。通常,$()
足够老,即使在旧版本的ksh中也可以使用,但是如果不行,则将"部分放在反引号中。