快速find一个文件中不在另一个文件中的行?
我有两个大文件(文件名集)。 每个文件大概有30,000行。 我试图find一个快速的方式findfile1中不存在的文件2中的行。
例如,如果这是file1:
line1 line2 line3
这是file2:
line1 line4 line5
那么我的结果/输出应该是:
line2 line3
这工作:
grep -v -f file2 file1
但是在我的大文件上使用它非常非常慢。
我怀疑有一个好方法,使用diff(),但输出应该只是线,没有别的,我似乎无法find一个开关。
任何人都可以帮助我find一个快速的方式,使用bash和基本的Linux二进制文件?
编辑:跟进我自己的问题,这是迄今为止我发现使用diff()的最佳方法:
diff file2 file1 | grep '^>' | sed 's/^>\ //'
当然,一定有更好的办法?
你可以通过在GNU diff
输出中控制旧/新/不变的行的格式来实现:
diff --new-line-format="" --unchanged-line-format="" file1 file2
应该对input文件进行sorting才能正常工作。 使用bash
(和zsh
),你可以使用进程replace<( )
就地sorting:
diff --new-line-format="" --unchanged-line-format="" <(sort file1) <(sort file2)
在上面的新的和不变的行被压制,所以只有输出改变 (即在你的情况下被删除的行)。 您也可以使用其他解决scheme不提供的几个diff
选项,例如-i
忽略大小写,或者使用各种空白选项( -E
, -b
, -v
等)进行较不严格的匹配。
说明
选项--new-line-format
, --old-line-format
--unchanged-line-format
--old-line-format
和--unchanged-line-format
使您可以控制diff
格式化差异的方式,类似于printf
格式说明符。 这些选项分别格式化新 (添加), 旧 (删除)和不变的行。 将其中一个设置为空“”可以防止这种行的输出。
如果您熟悉统一差异格式,您可以通过以下方式重新创build它:
diff --old-line-format="-%L" --unchanged-line-format=" %L" \ --new-line-format="+%L" file1 file2
%L
说明符是所讨论的行,我们在前面加上“+”“ – ”或“”,就像diff -u
(注意它只输出差异,缺less---
+++
和@@
行在每个分组更改的顶部)。 您也可以使用这个来做其他有用的事情,比如用%dn
每一行编号 。
diff
方法(连同其他build议comm
和join
)只会产生带有sortinginput的预期输出,尽pipe您可以使用<(sort ...)
sorting。 下面是一个简单的awk
(nawk)脚本(受Konsolebox答案中链接的脚本的启发),该脚本接受任意sorting的input文件, 并按照它们在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
运算符来确定file1中的行是否存在于file2中。 (如果有重复,这将有不同的输出到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[]
索引,另一个用行内容ss1[]
索引。 然后,当读取file2时,将从ll1[]
和ss1[]
删除每条匹配行。 最后输出file1的剩余行,保留原来的顺序。
在这种情况下,如上所述的问题,你也可以使用GNU split
(过滤是一个GNU扩展)进行分割和征服 ,每次完全重复运行file1块和读取file2块:
split -l 20000 --filter='gawk -f linesnotin.awk - file2' < file1
请注意,在gawk
命令行中使用和放置-
意味着stdin
。 这是通过从每个20000行的每块调用中分块的file1提供的。
对于非GNU系统的用户来说,几乎可以肯定有一个GNU coreutils包可以获得,包括OSX,作为提供GNU diff
, awk
的Apple Xcode工具的一部分,尽pipe只是POSIX / BSD split
而不是GNU版本。
comm命令(简称“common”)可能是有用的comm - compare two sorted files line by line
#find lines only in file1 comm -23 file1 file2 #find lines only in file2 comm -13 file1 file2 #find lines common to both files comm -12 file1 file2
man
文件实际上对此很可读。
像konsoleboxbuild议,海报grep的解决scheme
grep -v -f file2 file1
如果您只是添加-F
选项,实际上效果很好(快),将模式视为固定string而不是正则expression式。 我在一对〜1000行文件列表中validation了这一点,我不得不比较一下。 使用-F
花了0.031 s(实际),而没有花费2.278 s(实际),当grep输出redirect到wc -l
。
这些testing还包括-x
开关,这是解决scheme的必要部分,以确保在file2包含与file1中的一行或多行相匹配但不是全部行的情况下的完全准确性。
因此,不需要input进行sorting的解决scheme是快速,灵活(区分大小写等),以及(我认为)在任何POSIX系统上工作是:
grep -F -x -v -f file2 file1
什么是sorting和差异的速度?
sort file1 -u > file1.sorted sort file2 -u > file2.sorted diff file1.sorted file2.sorted
$ join -v 1 -t '' file1 file2 line2 line3
-t
确保它比较整个行,如果你有一些行的空间。
使用fgrep或添加-F选项来grep可以帮助。 但是为了更快的计算,你可以使用Awk。
你可以尝试以下Awk方法之一:
http://www.linuxquestions.org/questions/programming-9/grep-for-huge-files-826030/#post4066219
你可以使用Python:
python -c ' lines_to_remove = set() with open("file2", "r") as f: for line in f.readlines(): lines_to_remove.add(line.strip()) with open("f1", "r") as f: for line in f.readlines(): if line.strip() not in lines_to_remove: print(line.strip()) '