文件上的 diff3 没有发现差异:
$ grep -P '\[\[.*?\]\]' -o intro.tex | sort > A.txt
$ grep -P '\[\[.*?\]\]' -o intro.tex | sort | uniq > B.txt
$ grep '\\pnum %% \[\[' intro.tex | sed 's/\\pnum %% //' | sort > C.txt
$ diff3 A.txt B.txt C.txt | wc -l
0
diff3 在运行相同命令的进程替换上发现差异:
$ diff3 \
<(grep -P '\[\[.*?\]\]' -o intro.tex | sort) \
<(grep -P '\[\[.*?\]\]' -o intro.tex | sort | uniq) \
<(grep '\\pnum %% \[\[' intro.tex | sed 's/\\pnum %% //' | sort) | wc -l
95
为什么?有什么想法吗?
最小重现器:
$ echo test > a
$ diff3 a a a
$ diff3 <(cat a) <(cat a) <(cat a)
====1
1:1c
test
2:0a
3:0a
答案1
如果我们运行diff3
在strace -f
:
$ strace -qqfe execve -e signal=none diff3 a b c
execve("/usr/bin/diff3", ["diff3", "a", "b", "c"], 0x7ffef2bb2d78 /* 53 vars */) = 0
[pid 13360] execve("/usr/bin/diff", ["diff", "--horizon-lines=100", "--", "b", "c"], 0x7ffc019c6d50 /* 53 vars */) = 0
[pid 13361] execve("/usr/bin/diff", ["diff", "--horizon-lines=100", "--", "a", "c"], 0x7ffc019c6d50 /* 53 vars */) = 0
正如您所看到的diff3
,调用了diff
两次,第三个文件是两次调用的操作数之一。
对于 ksh 风格的<(...)
进程替换,文件是一个管道。
cmd1 <(cmd2)
是相同的:
cmd2 | cmd1 /dev/fd/0
除了使用非 0 的 fd 之外。
因此,对于 的第三个参数diff3
,第一个参数diff
将消耗整个输入,第二个参数将没有任何内容可供读取,因此文件将显示为空。
所以第三个参数至少不能是管道。
如果使用zsh
,您可以使用=(...)
使用临时文件而不是管道的进程替换形式:
diff3 <(cmd1) <(cmd2) =(cmd3)
(前 2 个仍然可以是管道)。
在你的情况下:
$ diff3 \
<(grep -Po '\[\[.*?\]\]' intro.tex | sort) \
<(grep -Po '\[\[.*?\]\]' intro.tex | sort -u) \
=(sed -n 's/\\pnum %% \[\[/[[/p' intro.txt | sort)
(我还删除了多余的grep
并将选项uniq
移到-o
非选项之前)。
您甚至可以使用以下方法进行因式分解:
(){ diff3 $1 <(uniq<$1) $2; } =(grep -Po '\[\[.*?\]\]' intro.tex) \
=(sed -n 's/\\pnum %% \[\[/[[/p' intro.txt | sort)
1 如果第三个参数被-
解释diff3
为读取 stdin,那么第二个参数不能是管道,因为diff
在这种情况下它会传递给两个调用。你会注意到手册上写着:”最多一个这三个文件名中的一个可能是-
,它告诉diff3
读取该文件的标准输入。”