为什么对进程替换的输入进行 diff3 会发现差异,而对文件中的相同数据进行 diff3 则没有发现差异?

为什么对进程替换的输入进行 diff3 会发现差异,而对文件中的相同数据进行 diff3 则没有发现差异?

文件上的 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

如果我们运行diff3strace -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读取该文件的标准输入。”

相关内容