如何将输入拆分到两个管道

如何将输入拆分到两个管道

我想做一些与此相同的事情:

some-expensive-command > /tmp/mytempfile
grep -v "pattern" /tmp/mytempfile >> output.txt
grep "pattern" /tmp/mytempfile | yet-another-command

最好是优雅的,不需要tempfile。我考虑过通过管道tee,但我能想到的最好的办法可能是将三条线中的两条结合起来,仍然需要中间存储:

some-expensive-command | tee /tmp/mytempfile | grep -v "pattern" >> output.txt
grep "pattern" /tmp/mytempfile | yet-another-command

答案1

问题读起来听起来你想要一个 stdin 重定向到两个不同的命令如果是这种情况,请利用tee加法过程替换:

some-expensive-command | tee >(grep 'pattern' > output.txt) >(grep -v 'pattern' | another-command)

进程替换实际上是在 bash 内部实现的匿名管道(在子进程级别)。我们还可以使用命名管道 + tee。例如,在终端 A 中执行

$ mkfifo named.fifo
$ cat /etc/passwd | tee named.fifo | grep 'root'

在另一个终端 B 上执行

$ grep -v 'root' named.fifo

另一种看待这个问题的方式是认识grep线模式匹配工具,因此通过一次读取行并在多个命令中使用同一行,我们可以实现完全相同的效果:

rm output.txt # get rid of file so that we don't add old and new output
some-expensive-command | while IFS= read -r line || [ -n "$line" ]; do
    printf "%s\n" "$line" | grep 'pattern' >> output.txt
    printf "%s\n" "$line" | grep -v 'pattern' | another-command
done
# or if another-command needs all of the output, 
# place `| another-comand` after `done` clause

另一种方法是放弃grep并使用更强大的方法,例如awk

some-expensive-command | awk '/pattern/{print >> "output.txt"}; !/pattern/{print}' | another-command.

实际上,不用担心使用临时文件,只要在使用后清理它们即可。如果有效,那就有效。

答案2

使用bash流程替代

some-command | tee >(grep "pat" | another-command >>out1) | grep -v "pat" >>out2

进程替换将some-command的输出分配给grep "pat"的输入,从而为您节省了临时文件。当然,数据仍然保存在文件中(总是如此),只是您不必关心这一点。如果您不想将another-command的输出保存在文件中,而是打印它,我建议只需切换两个命令列表。

另一个很好的信息来源:man bash/扩张

相关内容