反向搜索

45 command-line grep sed awk efficiency

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

cha*_*aos 47

tac /grep解决方案

tac file | grep whatever
Run Code Online (Sandbox Code Playgroud)

或者更有效一点:

grep whatever < <(tac file)
Run Code Online (Sandbox Code Playgroud)

500MB 文件的时间:

real    0m1.225s
user    0m1.164s
sys     0m0.516s
Run Code Online (Sandbox Code Playgroud)

sed/grep解决方案:

sed '1!G;h;$!d' | grep whatever
Run Code Online (Sandbox Code Playgroud)

500MB 文件的时间:10 多分钟后中止。

awk/grep解决方案:

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

500MB 文件的时间:

real    0m5.626s
user    0m4.964s
sys     0m1.420s
Run Code Online (Sandbox Code Playgroud)

perl/grep解决方案:

perl -e 'print reverse <>' file | grep whatever
Run Code Online (Sandbox Code Playgroud)

500MB 文件的时间:

real    0m3.551s
user    0m3.104s
sys     0m1.036s
Run Code Online (Sandbox Code Playgroud)

  • 如果您要提高效率,最好将 `tac` 放在 grep 之后。如果你有一个 10,000,000 行的文件,只有 2 个匹配项,`tac` 只需要反转 2 行,而不是 10m。`grep` 仍然需要通过任何一种方式来完成整个过程。 (7认同)
  • 如果你把 `tac` 放在 `grep` 之后,它会从管道中读取数据,因此无法搜索。如果找到的行数很大,这将降低效率(或完全失败)。 (3认同)
  • `sed`、`awk` 和`perl`(用这种方法)是不行的,因为它们从头读取文件,效率很低。我认为 `tac` 是正确的。 (2认同)
  • @val0x00ff `&lt; &lt;(tac filename)` 应该和管道一样快:在这两种情况下,命令都是并行运行的。 (2认同)

Ans*_*tel 17

此解决方案可能会有所帮助:

tac file_name | grep -e expression
Run Code Online (Sandbox Code Playgroud)

  • `tac` 是 GNU 命令。在大多数其他系统上,等价物是 `tail -r`。 (3认同)

zza*_*per 10

一旦找到第一个匹配项,它就会退出:

 tac hugeproduction.log | grep -m1 WhatImLookingFor
Run Code Online (Sandbox Code Playgroud)

下面给出了前两场比赛前后的 5 行:

 tac hugeproduction.log | grep -m2 -A 5 -B 5 WhatImLookingFor
Run Code Online (Sandbox Code Playgroud)

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

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

 tac hugeproduction.log | grep -F -m2 -A 5 -B 5 'ABC1234XYZ'
Run Code Online (Sandbox Code Playgroud)


cuo*_*glm 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;
Run Code Online (Sandbox Code Playgroud)

然后:

$ ./reverse-grep.pl pattern file
Run Code Online (Sandbox Code Playgroud)