你如何在Bash中区分两个管道?

时间:2008-12-05 23:30:56

标签: bash diff pipeline

如何在不使用Bash中的临时文件的情况下diff两个管道?假设您有两个命令管道:

foo | bar
baz | quux

你想在他们的输出中找到diff。一个解决方案显然是:

foo | bar > /tmp/a
baz | quux > /tmp/b
diff /tmp/a /tmp/b

是否可以在Bash中不使用临时文件的情况下这样做?您可以通过在其中一个管道中管道来消除一个临时文件:

foo | bar > /tmp/a
baz | quux | diff /tmp/a -

但是你不能同时将两个管道同时传输到diff中(至少不是以任何明显的方式)。是否有一些聪明的技巧涉及/dev/fd而不使用临时文件?

3 个答案:

答案 0 :(得分:136)

带有2个tmp文件的单行(不是你想要的)将是:

 foo | bar > file1.txt && baz | quux > file2.txt && diff file1.txt file2.txt

使用 bash ,您可以尝试:

 diff <(foo | bar) <(baz | quux)

 foo | bar | diff - <(baz | quux)  # or only use process substitution once

第二个版本会更清楚地通过显示
来提醒您输入的内容 -- /dev/stdin++ /dev/fd/63或其他内容,而不是两个编号的fds。


甚至命名管道也不会出现在文件系统中,至少在操作系统中,bash可以通过使用/dev/fd/63等文件名来实现进程替换,以获取命令可以打开的文件名,并从中读取实际读取的文件名。在执行命令之前bash设置的已打开文件描述符。 (即bash在fork之前使用pipe(2),然后dup2quux的输出重定向到diff的输入文件描述符,在fd 63上。)

在没有“魔法”/dev/fd/proc/self/fd的系统上,bash可能会使用命名管道来实现进程替换,但与临时文件不同,它至少会自行管理它们,并且您的数据不会不要写入文件系统。

您可以检查bash如何使用echo <(true)实现进程替换以打印文件名而不是从中读取。它在典型的Linux系统上打印/dev/fd/63。或者有关bash使用的系统调用的详细信息,Linux系统上的此命令将跟踪文件和文件描述符系统调用

strace -f -efile,desc,clone,execve bash -c '/bin/true | diff -u - <(/bin/true)'

如果没有bash,您可以创建命名管道。使用-告诉diff从STDIN读取一个输入,并使用命名管道作为另一个:

mkfifo file1_pipe.txt
foo|bar > file1_pipe.txt && baz | quux | diff file1_pipe.txt - && rm file1_pipe.txt

请注意,您只能使用tee命令将一个输出传送到多个输入

ls *.txt | tee /dev/tty txtlist.txt 

上面的命令显示ls * .txt到终端的输出,并将其输出到文本文件txtlist.txt。

但是通过流程替换,您可以使用tee将相同的数据提供给多个管道:

cat *.txt | tee >(foo | bar > result1.txt)  >(baz | quux > result2.txt) | foobar

答案 1 :(得分:117)

在bash中,您可以使用子shell,通过将管道封闭在括号内来单独执行命令管道。然后你可以用&lt;创建匿名命名管道,然后传递给diff。

例如:

diff <(foo | bar) <(baz | quux)

匿名命名管道由bash管理,因此它们会自动创建和销毁(与临时文件不同)。

答案 2 :(得分:5)

到达此页面的某些人可能正在寻找逐行差异,而应使用commgrep -f代替。

有一点需要指出的是,在所有答案的例子中,差异实际上不会在两个流完成之前开始。用例如:

进行测试
comm -23 <(seq 100 | sort) <(seq 10 20 && sleep 5 && seq 20 30 | sort)

如果这是一个问题,你可以尝试sd(流差异),它不需要排序(如comm那样),也不需要像上面的例子那样处理替换,是命令或数量更快比grep -f并支持无限流。

我建议的测试示例将在sd

中以这样的方式编写
seq 100 | sd 'seq 10 20 && sleep 5 && seq 20 30'

但不同之处在于seq 100会立即与seq 10区分开来。请注意,如果其中一个流是tail -f,则无法通过进程替换来完成差异。

这是我写的关于在终端上传播流的blogpost,它引入了sd