6

有没有办法使用ack作为-f选项从另一个文件中获取一个文件中的模式(模式列表) grep?我看到有一个-f选项 inack但它与-fin不同grep

也许一个例子会给你一个更好的主意。假设我有file1:

file1:
a
c
e

和文件2:

file2:
a  1
b  2
c  3
d  4
e  5

我想从 file2 中获取 file1 中的所有模式以给出:

a  1
c  3
e  5

可以ack这样做吗?否则,是否有更好的方法来处理这项工作(例如awk或使用哈希),因为我在两个文件中都有数百万条记录并且真的需要一种有效的方法来完成?谢谢!

4

5 回答 5

8

这是一个 Perl 单行程序,它使用散列来保存来自 file1 的一组想要的键,以便在 file2 的行上每次迭代进行 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

密钥集将保存在内存中,同时针对密钥逐行测试 file2。

使用 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) 摊销查找的哈希中时。与其他解决方案相比,此解决方案可能具有的优势是某些(较慢的)解决方案必须为 file2 的每一行运行一次您的密钥文件(file1)。这种解决方案将是 O(m*n),其中 m 是密钥文件的大小,n 是 file2 的大小。另一方面,这种散列方法提供了 O(m+n) 时间。这是一个巨大的差异。它通过消除通过键集的线性搜索而受益,并且通过仅通过 IO 读取一次键来进一步受益。

于 2012-03-30T04:49:51.283 回答
6

好吧,如果我们从评论切换到答案... ;-)

这是一个与 DavidO 的 perl 单行代码相同的 awk 单行代码,但在 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”的计算结果为真,缺少的大括号块被认为等同于{print},所以如果前一个块找到匹配项,这个块将打印当前行。

如果 file1 包含字符串而不是正则表达式,那么您可以通过将第一个比较替换为if(index($0,i))....

谨慎使用。你的旅费可能会改变。在可能包含坚果的设施中创建。

于 2012-03-30T05:01:28.263 回答
1

TXR可能是处理您的要求的另一种选择。我对它太陌生,无法在其中编写您需要的内容,但作者是 StackOverflow 的频繁贡献者。虽然我确信你可以用 TXR 做你需要的事情,但我不确定它会表现得更好。你需要测试一下。

值得一看,如果您对专门用于模式匹配的整个语言感兴趣。:)

于 2012-03-30T06:13:29.837 回答
1

您可以使用 tr 将文件转换为 ack 的正则表达式。我使用 sed 删除尾随管道字符。

ack "`tr '\n' '|' < patts | sed 's/.$//'`"

请注意,您需要几个过程,因此 awk 解决方案可能更有效,但这很容易记住。

于 2013-06-20T11:01:52.793 回答
1
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>
于 2012-03-30T05:41:53.310 回答