是否有一种方法可以使用ack
作为grep
中的-f
选项从另一个文件中获取一个文件(模式列表)中的模式?我看到ack
中有一个-f
选项,但它与grep
中的-f
不同。
也许一个例子会给你一个更好的想法。假设我有文件1:
file1:
a
c
e
和文件2:
file2:
a 1
b 2
c 3
d 4
e 5
我想从文件2中获得文件1中的所有模式,以给出:
a 1
c 3
e 5
ack
能做到这一点吗?否则,有没有更好的方法来处理作业(比如awk
或使用哈希),因为我在两个文件中都有数百万条记录,真的需要一种有效的方法来完成?谢谢
这里有一个Perl单行,它使用哈希来保存文件1中的一组所需密钥,以便在文件2的行上每次迭代进行O(1)(摊销时间)查找。因此它将在O(m+n)时间内运行,其中m是密钥集中的行数,n是正在测试的文件中的行数。
perl -ne'BEGIN{open K,shift@ARGV;chomp(@a=<K>);@hash{@a}=()}m/^(p{alpha}+)s/&&exists$hash{$1}&&print' tkeys file2
密钥集将保存在内存中,同时文件2将根据密钥逐行测试。
以下是使用Perl的-a
命令行选项的相同内容:
perl -ane'BEGIN{open G,shift@ARGV;chomp(@a=<G>);@h{@a}=();}exists$h{$F[0]}&&print' tkeys file2
第二个版本可能对眼睛更容易一些。)
这里你必须记住的一件事是,你更有可能是IO绑定的,而不是处理器绑定的。因此,目标应该是尽量减少IO的使用。当整个查找关键字集保存在提供O(1)个摊销查找的哈希中时。与其他解决方案相比,此解决方案的优势在于,某些(较慢的)解决方案必须为文件2的每一行运行一次密钥文件(文件1)。这种解决方案将是O(m*n),其中m是密钥文件的大小,n是文件2的大小。另一方面,这种散列方法提供了O(m+n)时间。这是一个巨大的差异。它通过消除对密钥集的线性搜索而受益,并且通过IO只读取一次密钥而进一步受益。
好吧,如果我们从评论切换到答案…;-)
这里有一个awk-one-liner,它的作用与DavidO的perl-one liner相同,但在awk中。Awk比Perl更小,而且可能更精简。但是awk有一些不同的实现。我不知道你的性能会比其他的好,还是比perl好。您需要进行基准测试。
awk 'NR==FNR{a[$0]=1;next} {n=0;for(i in a){if($0~i){n=1}}} n' file1 file2
这(应该)做什么
awk脚本的第一部分只匹配file1中的行(其中当前文件中的记录编号等于记录总数),并填充数组。第二部分(在后续文件上运行)遍历数组中的每个项,并查看它是否可以用作正则表达式来匹配当前输入行。
第二个代码块以"n"开头,在前一个代码块中将其设置为0或1。在awk中,"1"的计算结果为true,并且一个丢失的花括号块被认为等效于{print}
,因此如果前一个块找到了匹配项,则此块将打印当前行。
如果file1包含字符串而不是regexp,那么您可以通过将第一个比较替换为if(index($0,i))...
来更改它,使其运行得更快。
小心使用。您的里程数可能有所不同。在可能含有坚果的设施中创建。
nawk 'FNR==NR{a[$0];next}($1 in a)' file3 file4
测试:
pearl.384> cat file3
a
c
e
pearl.385> cat file4
a 1
b 2
c 3
d 4
e 5
pearl.386> nawk 'FNR==NR{a[$0];next}($1 in a)' file3 file4
a 1
c 3
e 5
pearl.387>
TXR可能是处理您的需求的另一个选项。我对它太陌生了,无法在其中编写您需要的内容,但作者是StackOverflow的经常贡献者。虽然我确信你可以用TXR做你需要的事情,但我不确定它会表现得更好。你需要测试一下。
如果你对专门用于模式匹配的整个语言感兴趣,那么值得一看。:)
您可以使用tr将文件转换为用于ack的正则表达式。我使用sed来删除尾部管道字符。
ack"`tr'\n'|'<patts|sed的/.$//'`"
请注意,您需要几个过程,因此awk解决方案可能更高效,但这很容易记住。