您可以通过控制GNU diff
输出中旧/新/不变行的格式来实现此目的:
diff --new-line-format="" --unchanged-line-format="" file1 file2
输入文件应进行排序以使其正常工作。使用bash
(和zsh
),您可以使用流程替换就地排序<( )
:
diff --new-line-format="" --unchanged-line-format="" <(sort file1) <(sort file2)
在上面,新行和未更改的行被抑制,因此仅输出更改(即,在您的情况下为删除的行)。你也可以使用一些diff
选项,其他解决方案不提供,如-i
忽略大小写,或各种空白选项(-E
,-b
,-v
对于不太严格的匹配等)。
说明
选项--new-line-format
,--old-line-format
并--unchanged-line-format
让您控制的方式diff
格式之间的差异,类似 printf
的格式说明。这些选项分别格式化新行(添加),旧行(已删除)和未更改的行。将其中一个设置为空“”会阻止此类行的输出。
如果您熟悉统一的diff格式,则可以使用以下方法部分地重新创建它:
diff --old-line-format="-%L" --unchanged-line-format=" %L" \
--new-line-format="+%L" file1 file2
该%L
说明符是有问题的行,我们每个前缀为“+”,“ - ”或“”,像diff -u
(注意,只是输出不同,它缺乏---
+++
与@@
线在每个分组改变的顶部)。您也可以使用此做其他有用的东西像数每行有%dn
。
该diff
方法(以及其他建议comm
和join
)仅会产生带有排序输入的预期输出,尽管您可以使用<(sort ...)
它进行就地排序。这是一个简单的awk
(nawk)脚本(受Konsolebox的答案中链接到的脚本的启发),它接受任意排序的输入文件,并按在file1中出现的顺序输出缺失的行。
# output lines in file1 that are not in file2
BEGIN { FS="" } # preserve whitespace
(NR==FNR) { ll1[FNR]=$0; nl1=FNR; } # file1, index by lineno
(NR!=FNR) { ss2[$0]++; } # file2, index by string
END {
for (ll=1; ll<=nl1; ll++) if (!(ll1[ll] in ss2)) print ll1[ll]
}
它将file1的全部内容逐行存储在一个行号索引数组中ll1[]
,并将file2的所有内容逐行存储在一个行内容索引关联数组中ss2[]
。读取两个文件之后,请遍历ll1
并使用in
运算符确定file2中是否存在file1中的行。(diff
如果有重复项,则该方法的输出将不同。)
如果文件太大而无法存储它们都导致内存问题,则可以通过仅存储file1并在读取file2的过程中删除匹配项来将CPU换为内存。
BEGIN { FS="" }
(NR==FNR) { # file1, index by lineno and string
ll1[FNR]=$0; ss1[$0]=FNR; nl1=FNR;
}
(NR!=FNR) { # file2
if ($0 in ss1) { delete ll1[ss1[$0]]; delete ss1[$0]; }
}
END {
for (ll=1; ll<=nl1; ll++) if (ll in ll1) print ll1[ll]
}
上面将file1的全部内容存储在两个数组中,一个数组由行号索引,一个数组ll1[]
由行content索引ss1[]
。然后在读取file2时,从ll1[]
和中删除每个匹配的行ss1[]
。最后,输出文件1的其余行,并保留原始顺序。
在这种情况下,与如所陈述的问题,也可以分而治之使用GNU split
(过滤是GNU扩展)中,用file1和读取文件2完全每次的块重复运行:
split -l 20000 --filter='gawk -f linesnotin.awk - file2' < file1
需要注意的使用和放置-
意义stdin
上的gawk
命令行。这是由split
file1以每次调用20000行的数据块提供的。
对于非GNU系统的用户,几乎肯定是GNU coreutils软件包可以获取,包括OSX的部分苹果的Xcode工具,它提供了GNU diff
,awk
虽然只有一个POSIX / BSD split
而不是一个GNU版本。
awk 'NR==FNR{a[$0];next}!($0 in a)' file2 file1 > out.txt