根据每行中的第一个标记比较 2 个大文件的最佳方法是什么?

What is the best way to compare 2 large files based off of the first token in each line?

我有 2 个大文件(每个大约 500k 行或 85mb)包含文件的校验和和文件路径本身。根据校验和获取文件之间差异的最佳方法是什么?我可以编写 Java 程序、脚本等,但目标是必须高效。

例如,我有 FileA:

ec7a063d3990cf7d8481952ffb45f1d8b490b1b5  /home/user/first.txt
e0f886f2124804b87a81defdc38ad2b492458f34  /home/user/second.txt

文件 B:

650bc1eb1b24604819eb342f2ebc1bab464d9210  /home/user/third.txt
ec7a063d3990cf7d8481952ffb45f1d8b490b1b5  /home/user/blah/dup.txt

我想输出两个包含文件 A 和 B 中唯一文件的文件。

唯一A

e0f886f2124804b87a81defdc38ad2b492458f34  /home/user/second.txt

唯一B

650bc1eb1b24604819eb342f2ebc1bab464d9210  /home/user/third.txt

在这种情况下,"first.txt" 和 "dup.txt" 相同,因为它们的校验和相同,所以我将其排除在外,因为它不是唯一的。最有效的方法是什么?文件没有以任何方式排序。

使用 sort 对每个文件进行排序,然后将结果与 comm 进行比较。同名手册页中解释了这两个命令的用法。

一个bash唯一的解决方案:

# create a file with a mark that help to find the source of the hash remove duplicate 
sed 's/^\([0-9a-f]*\)[^0-9a-f]/=A=/' FileA | sort | uniq -w 32 > FileA.mark
sed 's/^\([0-9a-f]*\)[^0-9a-f]/=B=/' FileB | sort | uniq -w 32 > FileB.mark

# sort the 2 files together , keep only unique hashs 
sort -t= FileA.mark FileB.mark | uniq -w 32 -c  >  HashCountFromAB

# if the count equal 1 ( provide by option -c from uniq )
# we use the mark to find the origin of the hash 

grep '^ *1 [0-9a-f]*=A=' HashCountFromAB > FileA.uniq
grep '^ *1 [0-9a-f]*=B=' HashCountFromAB > FileB.uniq

所以这是一个快速的答案,但效率不高:

$ join -v1 <(sort FileA) <(sort FileB) | tee UniqueA
e0f886f2124804b87a81defdc38ad2b492458f34 /home/user/second.txt

$ join -v2 <(sort FileA) <(sort FileB) | tee UniqueB
650bc1eb1b24604819eb342f2ebc1bab464d9210 /home/user/third.txt

join 命令通过键匹配来自两个排序文件的行(默认情况下是默认分隔符为 space 的第一个字段)。不过,上面的命令效率不高,因为我们对文件进行了两次排序:一次获取第一个文件的唯一值 (-v1),然后再次获取第二个文件的唯一值 (-v2)。我很快就会 post 进行一些改进。

您可以在一次调用中获取唯一的值,但原始文件丢失了。请参阅下面的代码:

$ join -v1 -v2 <(sort FileA) <(sort FileB)
650bc1eb1b24604819eb342f2ebc1bab464d9210 /home/user/third.txt
e0f886f2124804b87a81defdc38ad2b492458f34 /home/user/second.txt

至此,我们几乎有了答案。我们拥有两个文件中所有不匹配的文件。此外,我们只对每个文件进行了一次排序。我相信这是有效的。但是,您丢失了 "origin" 信息。我们可以使用此迭代或代码用 sed 标记行:

$ join -v1 -v2 <(sort FileA | sed s/$/\ A/ ) <(sort FileB | sed s/$/\ B/ )
650bc1eb1b24604819eb342f2ebc1bab464d9210 /home/user/third.txt B
e0f886f2124804b87a81defdc38ad2b492458f34 /home/user/second.txt A

至此,我们有了独特的条目,我们知道它们来自哪个文件。如果您必须将结果放在单独的文件中,我想您可以使用 awk(或更多 bash )来完成此操作。这是包含 awk 的代码的又一次迭代:

join -v1 -v2 <(sort FileA | sed s/$/\ A/ ) <(sort FileB | sed s/$/\ B/ ) |  awk '{ file="Unique"  ; print , > file }