Grepping reverso

44

Digamos, eu tenho um arquivo de texto muito grande (cerca de 10.000.000 linhas). Eu preciso grepdisso desde o final e salve o resultado em um arquivo. Qual é a maneira mais eficiente de realizar tarefas?

caos
fonte
10
Use tace greppara conseguir o que deseja.
Valentin Bajrami
1
Além das excelentes soluções publicadas, o GNU greppossui uma --max-count (number)opção que interrompe após um certo número de correspondências, o que pode ser interessante para você.
Ulrich Schwarz
@ val0x00ff você poderia dar uma olhada esta questão
c0rp
Você sabe quantos hits você terá? Quando você achar que o grep encontrará 3 linhas, inicie o grep e inverta depois.
Walter Walter

Respostas:

46

Solução tac / grep

tac file | grep whatever

Ou um pouco mais eficaz:

grep whatever < <(tac file)

Tempo com um arquivo de 500 MB:

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

Solução sed / grep :

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

Tempo com um arquivo de 500 MB: interrompido após mais de 10 minutos.

Solução awk / grep :

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

Tempo com um arquivo de 500 MB:

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

Solução perl / grep :

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

Tempo com um arquivo de 500 MB:

real    0m3.551s
user    0m3.104s
sys     0m1.036s
caos
fonte
2
sed, awke perl(com esse método) não estão OK, pois eles lêem o arquivo desde o início, o que é muito ineficiente. Suponho que tacfaça a coisa certa.
vinc17
1
@ vinc17 sim, as estatísticas de tempo apontam para o que você disse.
caos
2
@ val0x00ff < <(tac filename)Deve ser tão rápido quanto um pipe: nos dois casos, os comandos são executados em paralelo.
vinc17
7
Se você está buscando eficiência, seria melhor colocar o tacdepois do grep. Se você tiver um arquivo de 10.000.000 de linhas, com apenas 2 correspondências, tacprecisará reverter 2 linhas, e não 10m. grepainda vai ter que passar por tudo de qualquer maneira.
23414 Patrick
3
Se você colocar tacdepois do grep, ele estará lendo de um cano e, portanto, não poderá procurar. Isso tornará menos eficiente (ou falhará completamente) se o número de linhas encontradas for grande.
jjanes
17

Esta solução pode ajudar:

tac file_name | grep -e expression
Anveshak
fonte
3
tacé o comando GNU. Na maioria dos outros sistemas, o equivalente é tail -r.
Stéphane Chazelas
@ Stéphane: Em pelo menos alguns sistemas Unix, tail -ré limitado a um pequeno número de linhas, isso pode ser um problema.
RedGrittyBrick 23/07
1
@RedGrittyBrick, você tem alguma referência a isso ou pode dizer quais sistemas têm essa limitação?
Stéphane Chazelas
@ StéphaneChazelas, tail -r /etc/passwdfalha com tail: invalid option -- 'r'. Estou usando o coreutils-8.21-21.fc20.x86_64.
Cristian Ciupitu 23/07/2014
@CristianCiupitu, como eu disse, o GNU possui tac(e apenas o GNU possui tac) muitos outros Unices tail -r. O GNU tailnão oferece suporte-r
Stéphane Chazelas
10

Este sai assim que encontra a primeira correspondência:

 tac hugeproduction.log | grep -m1 WhatImLookingFor

A seguir, são apresentadas as 5 linhas antes e depois das duas primeiras partidas:

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

Lembre-se de não usar -i(sem distinção entre maiúsculas e minúsculas), a menos que você precise, pois isso desacelerará o grep.

Se você souber a string exata que está procurando, considere fgrep(String fixa)

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

Se o arquivo é muito grande, não pode caber na memória, vou usar Perlcom File :: ReadBackwards módulo de 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;

Então:

$ ./reverse-grep.pl pattern file
cuonglm
fonte
A vantagem dessa abordagem é que você pode ajustar o Perl para fazer o que quiser.
Zzapper
1
@zzapper: Também é eficiente em termos de memória, pois quando ele lê o arquivo linha por linha, em vez do arquivo slurp na memória, como tac.
cuonglm
alguém pode adicionar um suporte -m para isso? Eu gostaria de testar em arquivos reais. Veja: gist.githubusercontent.com/ychaouche/…
ychaouche 5/11/18