如何在bash脚本中分叉/管道stdin?

3

我有一个脚本,想要以以下方式运行:

$ myscript < mydata.dat

myscript中,我需要将标准输入(STDIN)分叉/管道传输到多个目的地。

#!/usr/bin/env bash

php script1.php > out1
php script2.php > out2
php script3.php > out3

每个人都需要 STDIN 的副本。这是可能的吗?

比如:

# obviously this is broken ...
STDIN | php script1.php > out1
STDIN | php script2.php > out2
STDIN | php script3.php > out3

我听说 https://www.gnu.org/software/parallel/ 是其中一种方法来实现这个。 - RutledgePaulV
2个回答

8

要将标准输入复制到多个进程,使用tee进程替换

tee >(script1 > out1) >(script2 >out2) ... | lastscript >outlast

构造函数>(...)称为进程替换。它创建了一个类似文件的对象,tee可以写入该对象。括号内的命令被执行,无论tee向其写入什么内容,都会将其作为stdin提供给该命令。 进程替换受bash、ksh和zsh支持。它不是POSIX标准,在dash下无法工作。

简单示例

让我们考虑这个简单的脚本:
$ cat myscript 
#!/bin/bash
tee >(grep 1 >out1) >(grep 2 >out2) | grep 3 >out3

我们可以运行它并验证结果:
$ seq 10 | bash myscript
$ cat out1
1
10
$ cat out2
2
$ cat out3
3

1
你不需要多个 tee 命令。你可以给单个命令提供多个 >(...) 参数。 - Barmar
你能更详细地解释一下 >(...) 吗?我从未见过这种写法。 - Mulan
@naomik 我在最后一次更新中添加了一些解释。你可能需要刷新浏览器才能看到它。如果有帮助,请告诉我。 - John1024

3
John1024的回答有个变体,可以使用命名管道来替代进程置换。
mkfifo p1 p2 p3
tee p1 p2 p3 > /dev/null & # or tee p1 p2 > p3 &
php script1 < p1 > out1 &
php script2 < p2 > out2 &
php script2 < p3 > out3 &
wait
rm p1 p2 p3

进程替换实际上是,有些实现方式是字面上的语法支持,用于创建、管理和清理显式命名管道。

很酷看到非快捷方式版本。我非常感激。 - Mulan
3
@chepner,这个问题在于它们是在文件系统中创建的,并且容易与其他进程发生文件名冲突以及权限失败。可能我们应该使用临时文件来使解决方案更加健壮。 - Alexey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接