将输出导向两个不同的命令

38

可能是重复问题:
osx/linux: pipes into two processes?

是否有一种方法将一个命令的输出导入到另外两个命令的输入中,同时运行它们?

像这样:

$ echo 'test' |(cat) |(cat)
test
test
我之所以想要这样做是因为我有一个程序可以从USB SDR设备接收FM电台信号,并将音频输出为原始PCM数据(类似于.wav文件但没有头部)。由于信号不是音乐而是POCSAG寻呼机数据,我需要将其导入解码器程序以恢复寻呼文本。但是,我也想听这个信号,以便知道是否有任何数据进来或者没有数据被广播。(否则我就无法判断解码器是否损坏或只是没有数据正在广播)。因此,除了将数据导入到寻呼解码器中,我还需要将相同的数据导入到play命令。

目前我只知道如何处理其中之一——将其导入解码器并静默读取数据,或将其导入play并听到声音,但看不到任何解码文本。

我该如何将相同的数据导入两个命令,以便我既能读取文本又能听到音频?

我不能使用tee,因为它只会将重复的数据写入文件,而我需要实时处理数据。


4
例如,请参见http://unix.stackexchange.com/questions/28503/how-can-i-send-stdout-to-multiple-commands,该网站上讨论了如何将标准输出发送给多个命令的问题。 - Mat
@Mat:你能在这里重新发布你的答案吗?那对我很有用,看起来是一个非常好的解决方案。 - Malvineous
1
是的,我认为这是那个的重复。 - Malvineous
如果你只是想在一个命令中避免多次输入某个内容,那么最简单的解决方法就是 x=myoutput && command one $x && command two $x - tumelo
@tumelo:这个问题是关于通过标准输入传递数据,但您的示例仅将数据放置在命令行参数中,它没有将其传递给stdin上的命令。 - Malvineous
3个回答

43

如果同时使用teemkfifo应该没问题。

mkfifo pipe
cat pipe | (command 1) &
echo 'test' | tee pipe | (command 2)

2
这在例如备份时非常有用,可以节省IO。可以将tar输出通过pvsha512sum管道传输,然后再写入,避免对磁盘进行双重甚至三重读/写操作。 - anon
2
顺便提一下,由于这可能会变得非常混乱,如果您想在某个命令上失败,您必须编写{ somecommand || errorhandler; } | tee pipe | command2,甚至是{ somecommand || errorhandlerforcommand1; } | tee pipe | command2 || errorhandlerforcommand2,因为somecommand || errorhandler | tee pipe | command2somecommand | tee pipe | command2 || errorhandler都不能像人们期望的那样处理somecommand的错误! - anon
1
最后,要将仅限于stderr的输出流传输至日志处理程序,必须在||之前添加2> >(errlogger),方法如下:{ somecommand 2> >(errlogger) || errorhandlerforcommand1; } | tee pipe | command2。否则会出现错误。 - anon

29

最近的 中出现了新的 >(command) 语法:

echo "Hello world." | tee >(sed 's/^/1st: /')  >(sed 's/^/2nd cmd: /') >/dev/null

可能会返回:

2nd cmd: Hello world.
1st: Hello world.

下载somefile.ext,保存文件,计算md5sum和sha1sum:

wget -O - http://somewhere.someland/somepath/somefile.ext |
    tee somefile.ext >(md5sum >somefile.md5) | sha1sum >somefile.sha1
或者
wget -O - http://somewhere.someland/somepath/somefile.ext |
    tee >(md5sum >somefile.md5) >(sha1sum >somefile.sha1) >somefile.ext

旧答案

有一种方法可以通过无名管道来实现这一点(在Linux下测试过):

 (( echo "hello" |
         tee /dev/fd/5 |
             sed 's/^/1st occure: /' >/dev/fd/4
    ) 5>&1 |
    sed 's/^/2nd command: /'
 ) 4>&1

给予:

2nd command: hello
1st occure: hello

这个示例将让你下载somefile.ext,保存它们,计算它的md5sum和sha1sum:

(( wget -O - http://somewhere.someland/somepath/somefile.ext |
    tee /dev/fd/5 |
    md5sum >/dev/fd/4
  ) 5>&1 |
  tee somefile.ext |
  sha1sum
) 4>&1

请阅读我在此重复问题的更详细回答 - F. Hauri - Give Up GitHub
或者使用我的 parallel.sh 使用bash并行化流处理 - F. Hauri - Give Up GitHub
如果这个多重语法对于tee起作用,为什么它甚至在没有tee的情况下也不能起作用呢? - einpoklum
@einsupportsModeratorStrike 这种语法是 bashism,适用于任何命令!tee 的目的是将输出复制到多个目标。 - F. Hauri - Give Up GitHub
但是为什么我们需要tee呢,如果我们可以使用>()呢?也就是说,为什么不只是echo "Hello world." >(sed 's/^/1st: /') >(sed 's/^/2nd cmd: /') - einpoklum
@einsupportsModeratorStrike 因为echo使用你的STDOUT作为输出,这是唯一的!你不能重定向超过一次的任何输出!tee旨在将输出复制到你指定的多个目标,以及STDOUT - F. Hauri - Give Up GitHub

6
也许可以看一下 tee 命令。它的作用是将输入内容简单地输出到文件,同时也将其输出到标准输出。因此可以像这样使用:
echo "Hello" | tee try.txt | <some_command>

将创建一个包含“Hello”内容的文件,并且还会让“Hello”(通过管道)成为<some_command>的标准输入。

2
我不能使用tee,因为它只会将重复的数据写入文件,但我需要实时处理数据。 - cnicutar
也许我误解了你。难道你不需要传递原始数据来播放吗? - Ivaylo Strandjev
@izomorphius:我需要实时听取数据-如果将其重定向到文件中,会有延迟,因此它将不再是实时的。而且,如果我长时间监听,文件将不断增大,直到磁盘空间耗尽! - Malvineous
1
但是你可以使用 tee 将输出重定向到一个管道中,它不一定非要是一个文件。 - Ivaylo Strandjev
3
@izomorphius:啊,是的,但我忘记了管道,这方面在你的回答中很巧妙地被省略掉了 :-P (翻译):@izomorphius:啊,是的,但我忘记了管道,这一点方便地没有在你的回答中提到:-P - Malvineous

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