Обратный grepping

Скажем, у меня действительно большой текстовый файл (около 10.000.000 строк). Мне нужно grep его с конца и сохранить результат в файл. Каков наиболее эффективный способ выполнения задачи?

Решение tac / grep

 tac file | grep whatever 

Или немного более эффективно:

 grep whatever < <(tac file) 

Время с файлом 500 МБ:

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

sed / grep Решение:

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

Время с файлом 500 МБ: отменено через 10+ минут.

awk / grep Решение:

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

Время с файлом 500 МБ:

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

perl / grep Решение:

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

Время с файлом 500 МБ:

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

Это решение может помочь:

 tac file_name | grep -e expression 

Этот выход завершается, как только он находит первый матч:

  tac hugeproduction.log | grep -m1 WhatImLookingFor 

Ниже приведены 5 строк до и после первых двух совпадений:

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

Помните, что нельзя использовать -i (без учета регистра), если только вам это не понадобится, поскольку это замедлит работу grep.

Если вы знаете точную строку, которую вы ищете, рассмотрим fgrep (Fixed String)

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

Если файл действительно большой, он не может поместиться в память, я буду использовать Perl с модулем File :: 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