反向grepping


44

假设我有一个很大的文本文件(大约10.000.000行)。我需要grep从头开始并将结果保存到文件中。完成任务的最有效方法是什么?


10
使用tacgrep实现您想要的。
Valentin Bajrami 2014年

1
除了出色的解决方案之外,GNU grep还有一个--max-count (number)开关,在经过一定数量的匹配后,该开关将中止,这可能对您来说很有趣。
Ulrich Schwarz 2014年

@ val0x00ff您能否看一下这个问题
c0rp

你知道你会有多少点击吗?当您认为自己的grep将找到3行时,请开始进行grepping,然后反转。
Walter A

Answers:


46

tac / grep解决方案

tac file | grep whatever

或更有效:

grep whatever < <(tac file)

拥有500MB文件的时间:

real    0m1.225s
user    0m1.164s
sys     0m0.516s

sed / grep解决方案:

sed '1!G;h;$!d' | grep whatever

拥有500MB文件的时间:超过10分钟后中止。

awk / grep解决方案:

awk '{x[NR]=$0}END{while (NR) print x[NR--]}' file | grep whatever

拥有500MB文件的时间:

real    0m5.626s
user    0m4.964s
sys     0m1.420s

perl / grep解决方案:

perl -e 'print reverse <>' file | grep whatever

拥有500MB文件的时间:

real    0m3.551s
user    0m3.104s
sys     0m1.036s

2
sedawkperl(使用此方法)不正确,因为它们从头开始读取文件,效率非常低。我认为这样tac做是对的。
vinc17 2014年

1
@ vinc17是的,时间统计信息指向您所说的内容。
混乱

2
@ val0x00ff < <(tac filename)应该与管道一样快:在两种情况下,命令都是并行运行的。
vinc17 2014年

7
如果您要提高效率,最好将tacgrep 放在后面。如果您有一个10,000,000行的文件,并且只有2个匹配项,tac则只需要反转2行,而不是10m。grep无论如何,仍然需要经历整个过程。
Patrick

3
如果放在tac之后grep,它将从管道读取,因此无法查找。如果找到的行数很多,这将使其效率降低(或完全失败)。
jjanes 2014年

17

此解决方案可能会帮助:

tac file_name | grep -e expression

3
tac是GNU命令。在大多数其他系统上,等效项是tail -r
斯特凡Chazelas

@Stéphane:在至少某些Unix系统上,tail -r限于少数行,这可能是一个问题。
RedGrittyBrick 2014年

1
@RedGrittyBrick,您对此有任何参考,还是请告诉哪些系统有此限制?
斯特凡Chazelas

@StéphaneChazelas,tail -r /etc/passwd失败tail: invalid option -- 'r'。我正在使用coreutils-8.21-21.fc20.x86_64。
Cristian Ciupitu 2014年

就像我说的,@ CristianCiupitu,GNU还有tac(只有GNU有tac)许多其他Unices都有tail -r。GNU tail不支持-r
斯特凡Chazelas

10

找到第一个匹配项后立即退出:

 tac hugeproduction.log | grep -m1 WhatImLookingFor

下面给出了前两个匹配之前和之后的5行:

 tac hugeproduction.log | grep -m2 -A 5 -B 5 WhatImLookingFor

切记不要使用-i(不区分大小写),除非您必须这样做,否则会降低grep的速度。

如果您知道要查找的确切字符串,请考虑fgrep(固定字符串)

 tac hugeproduction.log | grep -F -m2 -A 5 -B 5 'ABC1234XYZ'

9

如果文件确实很大,无法容纳在内存中,我将PerlFile :: ReadBackwards模块一起使用CPAN

$ cat reverse-grep.pl
#!/usr/bin/perl

use strict;
use warnings;

use File::ReadBackwards;

my $pattern = shift;
my $rev = File::ReadBackwards->new(shift)
    or die "$!";

while (defined($_ = $rev->readline)) {
    print if /$pattern/;
}

$rev->close;

然后:

$ ./reverse-grep.pl pattern file

这种方法的优点是您可以调整Perl以执行您想要的任何事情。
zzapper 2014年

1
@zzapper:它的内存效率也很高,因为当它逐行读取文件而不是像那样在内存中读取文件时,它是有效的tac
cuonglm 2014年

任何人都可以为此添加-m支持吗?我想测试真实文件。请参阅:gist.githubusercontent.com/ychaouche/...
ychaouche
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.