我想将进程proc1的stdout重定向到两个进程proc2和proc3:
proc2 -> stdout
/
proc1
\
proc3 -> stdout
我试过
proc1 | (proc2 & proc3)
但它似乎不起作用,即
echo 123 | (tr 1 a & tr 1 b)
写入
b23
到stdout而不是
a23
b23
答案 0 :(得分:120)
编者注::
- >(…)
是process substitution,是某些 POSIX兼容shell的非标准shell功能:bash
,{{1} },ksh
。
- 这个答案意外地通过管道发送输出过程替换的输出:zsh
。
- 流程替换的输出将是不可预测的交错,除了echo 123 | tee >(tr 1 a) | tr 1 b
之外,管道可能在zsh
内的命令之前终止。
在unix(或在Mac上),使用tee
command:
>(…)
通常你会使用$ echo 123 | tee >(tr 1 a) >(tr 1 b) >/dev/null
b23
a23
将输出重定向到多个文件,但使用>(...)你可以
重定向到另一个进程。所以,一般来说,
tee
会做你想做的事。
在Windows下,我认为内置shell没有等价物。微软的Windows PowerShell有一个$ proc1 | tee >(proc2) ... >(procN-1) >(procN) >/dev/null
命令。
答案 1 :(得分:22)
与dF说的一样,bash
允许使用>(…)
构造来运行命令来代替文件名。 (还有<(…)
构造替换另一个命令的输出来代替文件名,但现在这是无关紧要的,我只是为了完整性而提到它。
如果您没有bash,或者在使用旧版本bash的系统上运行,则可以通过使用FIFO文件手动执行bash的操作。
实现目标的一般方法是:
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
for i in $subprocesses do tr 1 $i </tmp/pipe.$mypid.$i & # background! done
proc1 | tee $(for i in $subprocesses; do echo /tmp/pipe.$mypid.$i; done)
for i in $subprocesses; do rm /tmp/pipe.$mypid.$i; done
注意:出于兼容性原因,我会使用反引号进行$(…)
,但我无法写这个答案(反引用在SO中使用)。通常情况下,$(…)
已经足够老了,甚至可以在旧版本的ksh中工作,但如果没有,请将…
部分包含在反引号中。
答案 2 :(得分:7)
bash
,ksh
,zsh
) dF.'s answer包含基于tee
和输出 process substitutions的答案的种子
(>(...)
)可能会或可能不会工作,具体取决于您的要求:
请注意,进程替换是非标准功能(主要)
POSIX-features-only shell,例如dash
(在Ubuntu上充当/bin/sh
,
例如),做不支持。定位/bin/sh
的Shell脚本应该不依赖它们。
echo 123 | tee >(tr 1 a) >(tr 1 b) >/dev/null
这种方法的陷阱是:
不可预测的异步输出行为:输出流程替换>(...)
内的命令的输出流以不可预测的方式交错。
在bash
和ksh
中(与zsh
相对 - 但请参见下面的例外情况):
bash
和ksh
不等待输出进程替换生成的进程完成,至少在默认情况下是这样。 请注意,
>(...)
内启动的命令与原始shell分离,您无法轻松确定它们何时完成;写入所有内容后tee
将完成,但替换进程仍将消耗内核和文件I / O中各种缓冲区的数据,以及内部数据处理所花费的时间。如果你的外壳继续依赖子过程产生的任何东西,你可能会遇到竞争条件。
zsh
是 默认的唯一shell,等待输出流程替换中的进程完成,除了,如果 stderr 被重定向到一个(2> >(...)
)。
ksh
(至少从版本93u+
开始)允许使用无参数wait
等待输出进程替换 - 生成完成的过程。
但请注意,在交互式会话中,也可能导致等待任何挂起的后台作业。
bash v4.4+
可以等待最近启动的输出流程替换wait $!
,但无参数{{1} 不工作,使其不适合使用多个输出流程替换的命令。
但是, wait
和bash
可以强制等待,方法是将命令汇总到 {{1} } ,但请注意,这会使命令在子shell 中运行。 注意事项:
ksh
(自| cat
起)并不支持将 stderr 发送到输出流程替换(ksh
);这样的尝试是默默忽略。
虽然ksh 93u+
(值得称道)默认同步 stdout 输出流程替换,但是{{1}技术不能使它们与 stderr 输出过程替换(2> >(...)
)同步。
但是,即使您确保同步执行,不可预测的交错输出的问题仍然存在。
以下命令在zsh
或| cat
中运行时,说明了有问题的行为(您可能需要多次运行才能看到两个症状):{ {1}}通常会在输出替换的输出之前打印,并且后者的输出可以不可预测地交错。
2> >(...)
简而言之:
保证特定的每命令输出序列:
bash
,ksh
和AFTER
都不支持。同步执行:
printf 'line %s\n' {1..30} | tee >(cat -n) >(cat -n) >/dev/null; echo AFTER
中,他们总是异步。bash
,他们根本无法工作。如果您可以忍受这些限制,那么使用输出流程替换是一个可行的选择(例如,如果它们都写入单独的输出文件)。
请注意, tzot's much more cumbersome, but potentially POSIX-compliant solution也表现出不可预测的输出行为;但是,通过使用ksh
,您可以确保后续命令在所有后台进程完成后才开始执行
参见 更强大,同步,序列化的输出实现。
具有可预测输出行为 的唯一简单 zsh
解决方案如下所示,然而,令人望而却步大输入集慢,因为shell循环本质上很慢
另请注意,此 会替换目标命令的输出行。
zsh
通过序列化(按命令)输出,安装GNU parallel
可启用强大解决方案,此外还允许并行执行:
ksh
默认情况下, wait
可确保不同命令的输出不会交错(可以修改此行为 - 请参阅bash
)。
注意:某些Linux发行版带有不同的 while IFS= read -r line; do
tr 1 a <<<"$line"
tr 1 b <<<"$line"
done < <(echo '123')
实用程序,它不能使用上面的命令;使用$ echo '123' | parallel --pipe --tee {} ::: 'tr 1 a' 'tr 1 b'
a23
b23
来确定您拥有哪一个。
Jay Bazuzi's helpful answer显示如何在 PowerShell 中执行此操作。这就是说:他的答案是上面循环parallel
答案的模拟,对于大输入集, 交替输出过于缓慢来自目标命令的行。
man parallel
- 基于同步执行和输出序列化的便携式Unix解决方案以下是tzot's answer中提出的方法的一个简单但相当强大的实现,另外提供:
虽然不是严格遵守POSIX,但因为它是一个parallel
脚本,它应该可移植到任何具有parallel --version
的Unix平台。
注意:您可以在this Gist的MIT许可下找到更全面的实施。
如果您将下面的代码保存为脚本bash
,将其设置为可执行文件并将其放入bash
,则问题中的命令将如下所示:
bash
bash
脚本源代码:
fanout
答案 3 :(得分:5)
由于@dF:提到PowerShell有T恤,我想我会在PowerShell中展示一种方法。
PS > "123" | % {
$_.Replace( "1", "a"),
$_.Replace( "2", "b" )
}
a23
1b3
请注意,在创建下一个对象之前,将处理第一个命令中出现的每个对象。这可以允许缩放到非常大的输入。
答案 4 :(得分:1)
您还可以将输出保存在变量中,并将其用于其他进程:
out=$(proc1); echo "$out" | proc2; echo "$out" | proc3
但是,仅在
时有效proc1
在某点终止:-)proc1
不会产生太多输出(不知道有什么限制,但可能是您的RAM)但是很容易记住,并且可以从在此生成的进程获得的输出上有更多选择,例如e。 g。:
out=$(proc1); echo $(echo "$out" | proc2) / $(echo "$out" | proc3) | bc
我很难用| tee >(proc2) >(proc3) >/dev/null
的方法来做类似的事情。
答案 5 :(得分:-1)
另一种方法是,
eval `echo '&& echo 123 |'{'tr 1 a','tr 1 b'} | sed -n 's/^&&//gp'`
输出:
a23
b23
无需在此处创建子shell