bash – 在一个文件中找到不在另一个文件中的快捷方式?

前端之家收集整理的这篇文章主要介绍了bash – 在一个文件中找到不在另一个文件中的快捷方式?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有两个大文件(文件名集)。每个文件大约30,000行。我试图找到一种快速的方式找到file1中不存在于file2中的行。

例如,如果这是file1:

line1
line2
line3

这是file2:

line1
line4
line5

然后我的结果/输出应该是:

line2
line3

这工作:

grep -v -f file2 file1

但它是非常,非常缓慢,当我使用在我的大文件

我怀疑有一个很好的方法来做到这一点使用diff(),但输出应该只是行,没有别的,我似乎找不到一个开关。

任何人都可以帮助我找到一个快速的方式,这样做,使用bash和基本的linux二进制文件

编辑:为了跟进我自己的问题,这是我发现到目前为止使用diff()的最好的方式:

diff file2 file1 | grep '^>' | sed 's/^>\ //'

当然,必须有更好的方法吗?

你可以通过控制GNU diff输出中旧/新/不变的行的格式化来实现:
diff --new-line-format="" --unchanged-line-format=""  file1 file2

输入文件应该排序为这个工作。使用bash(和zsh),您可以使用进程替换<():

diff --new-line-format="" --unchanged-line-format="" <(sort file1) <(sort file2)

在上面的新的和不变的行被抑制,所以只有改变(即在你的情况下删除行)输出

说明

选项–new-line-format,–old-line-format和–unchanged-line-format允许您控制diff格式化差异的方式,类似于printf格式说明符。这些选项分别格式化新(添加),旧(删除)和不变的行。将一个设置为空“”防止输出这种线。

如果您熟悉统一差异格式,您可以使用以下部分重新创建:

diff --old-line-format="-%L" --unchanged-line-format=" %L" \
     --new-line-format="+%L" file1 file2

%L说明符是正在讨论的行,我们前面每个都有“”“ – ”或“”,像diff -u
(注意,它只输出差异,它缺少每个分组变化顶部的—和@@线)。
你也可以使用它来做其他有用的事情,如number each line与%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运算符确定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包可以获得,包括作为Apple Xcode工具的一部分在OSX上提供GNU diff,awk,虽然只有一个POSIX / BSD拆分,而不是一个GNU版本。

猜你在找的Bash相关文章