使用ack或awk或比grep更好的方法从另一个文件中获取一个文件的模式



是否有一种方法可以使用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解决方案可能更高效,但这很容易记住。

最新更新