假设我们有两个文本文件,我们想将它们合并为一个。
第二个文件以第一个文件中的行开头,因此它会重复其中的一部分。存在冗余重叠。
我怎样才能合并这些文件?
当然,理想情况下,欢迎针对一般二进制文件和多个部分的解决方案。
答案1
看似简短而甜蜜的解决方案是无处不在但也不祥的 awk 重复删除器:
awk '!x[$0]++'
它不仅删除重复项,还保留输入文件的原始顺序。这是该命令如何工作的解释:awk '!a[$0]++' 是如何工作的?
像这样使用
awk '!x[$0]++' file1 file2
它将先打印 file1,然后打印 file2,而不会重叠。由于重叠是重复的,因此将其删除。
但要小心!它将删除全部文件中的重复项。观察:
$ cat file1
a
b
b
overlap1
overlap2
$ cat file2
overlap1
overlap2
p
q
q
$ awk '!x[$0]++' file1 file2
a
b
overlap1
overlap2
p
q
它还删除了不重叠的重复行。
如果您的文件没有重复,或者您无论如何都想删除它们,那么这个命令就可以了。
如果您想保留重复的行,请继续阅读。
这是一种手动删除重叠而不删除重复项的方法。它可以自动化,但我没有付出努力。
首先从第一个文件中获取最后一行:
$ tail -n1 file1
overlap2
现在删除第二个文件中该行之前的所有行:
$ sed '0,/overlap2/d' file2
p
q
q
将 file1 与第二个命令的结果连接起来,您将得到连接的文件,没有重叠,但保留了重复项
$ cat file1 <(sed '0,/overlap2/d' file2)
a
b
b
overlap1
overlap2
p
q
q
这很有效,并且可能在大多数情况下都会有效。
但要小心!如果重叠有重复,这不会删除所有重叠。观察:
$ cat evil1
a
b
overlap1
overlap2
overlap3
overlap1
overlap2
$ cat evil2
overlap1
overlap2
overlap3
overlap1
overlap2
p
overlap2
q
确定第一个文件的最后一行
$ tail -n1 evil1
overlap2
从第二个文件中删除第一次出现的位置
$ sed '0,/overlap2/d' evil2
overlap3
overlap1
overlap2
p
overlap2
q
当重叠中存在重复时,仅删除第一次出现的位置并不能删除所有重叠。但由于杂散overlap2
线,我们也不能只删除最后一次出现的情况。
那么如何确定最大重叠呢?首先在 file2 中查找 file1 中最后一行的每一次出现。对于每个出现测试是否重叠。然后取最后一次仍然重叠的事件。
找到每一个出现的地方
$ grep -n overlap2 evil2
2:overlap2
5:overlap2
7:overlap2
测试每个是否重叠
$ diff -q <(tail -n2 evil1) <(head -n2 evil2)
$ diff -q <(tail -n5 evil1) <(head -n5 evil2)
$ diff -q <(tail -n7 evil1) <(head -n7 evil2)
Files /dev/fd/63 and /dev/fd/62 differ
没有输出意味着没有区别。两条线重叠。五行也是重叠的。但七条线不再重叠。这意味着第 5 行的出现是最大重叠,而第 7 行的出现与重叠无关。
$ cat evil1 <(sed '1,5d' evil2)
a
b
overlap1
overlap2
overlap3
overlap1
overlap2
p
overlap2
q
正如所说,这可以自动化,但我没有付出努力。
相关xkcd:https://xkcd.com/974/
至少我为这个答案付出了努力。享受。
答案2
如果顺序不重要,您可以使用以下命令来完成工作:
sort -u FILE1 FILE2 > FILE3
如果文件的顺序很重要,请使用以下命令:
cat -n FILE1 FILE2 | sort -uk2 | sort -nk1 | cut -f2- > FILE3