Grepping inverso

44

Digamos que tengo un archivo de texto realmente grande (aproximadamente 10,000,000 líneas). Lo necesito grepdesde el final y guardo el resultado en un archivo. ¿Cuál es la forma más eficiente de realizar la tarea?

caos
fuente
10
Usa tacy greppara lograr lo que quieres.
Valentin Bajrami
1
Además de las excelentes soluciones publicadas, GNU greptiene un --max-count (number)interruptor que se cancela después de un cierto número de coincidencias, lo que puede ser interesante para usted.
Ulrich Schwarz
@ val0x00ff podría echar un vistazo a esta pregunta
c0rp
¿Sabes cuántos golpes tendrás? Cuando creas que tu grep encontrará 3 líneas, comienza a grepping y retrocede después.
Walter A

Respuestas:

46

Solución tac / grep

tac file | grep whatever

O un poco más efectivo:

grep whatever < <(tac file)

Tiempo con un archivo de 500 MB:

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

Solución sed / grep :

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

Tiempo con un archivo de 500 MB: cancelado después de más de 10 minutos.

Solución awk / grep :

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

Tiempo con un archivo de 500 MB:

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

Solución perl / grep :

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

Tiempo con un archivo de 500 MB:

real    0m3.551s
user    0m3.104s
sys     0m1.036s
caos
fuente
2
sed, awky perl(con este método) no están bien ya que leen el archivo desde el principio, lo cual es muy ineficiente. Supongo que eso tachace lo correcto.
vinc17
1
@ vinc17 sí, las estadísticas de tiempo apuntan a lo que dijiste.
caos
2
@ val0x00ff El < <(tac filename)debería ser tan rápido como una tubería: en ambos casos, los comandos se ejecutan en paralelo.
vinc17
77
Si buscas eficiencia, sería mejor poner tacel grep después. Si tiene un archivo de 10,000,000 líneas, con solo 2 coincidencias, tacsolo tendrá que invertir 2 líneas, no 10m. greptodavía va a tener que pasar por todo el asunto de cualquier manera.
Patrick
3
Si coloca tacdespués de grep, se leerá desde una tubería y, por lo tanto, no puede buscar. Eso lo hará menos eficiente (o fallará por completo) si el número de líneas encontradas es grande.
jjanes
17

Esta solución podría ayudar:

tac file_name | grep -e expression
Anveshak
fuente
3
taces el comando GNU En la mayoría de los otros sistemas, el equivalente es tail -r.
Stéphane Chazelas
@ Stéphane: al menos en algunos sistemas Unix, tail -restá limitado a un pequeño número de líneas, esto podría ser un problema.
RedGrittyBrick
1
@RedGrittyBrick, ¿tiene alguna referencia para eso, o podría decir qué sistemas tienen esa limitación?
Stéphane Chazelas
@ StéphaneChazelas, tail -r /etc/passwdfalla con tail: invalid option -- 'r'. Estoy usando coreutils-8.21-21.fc20.x86_64.
Cristian Ciupitu
@CristianCiupitu, como dije, GNU tiene tac(y solo GNU tiene tac) muchas otras unidades tienen tail -r. GNU tailno es compatible-r
Stéphane Chazelas
10

Éste sale tan pronto como encuentra la primera coincidencia:

 tac hugeproduction.log | grep -m1 WhatImLookingFor

A continuación se muestran las 5 líneas antes y después de los dos primeros partidos:

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

Recuerde no usar -i(no distingue entre mayúsculas y minúsculas) a menos que tenga que hacerlo, ya que eso reducirá la velocidad.

Si conoce la cadena exacta que está buscando, considere fgrep(Cadena fija)

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

Si el archivo es muy grande, no puede caber en la memoria, voy a utilizar Perlcon archivos :: ReadBackwards módulo desde 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;

Entonces:

$ ./reverse-grep.pl pattern file
Cuonglm
fuente
La ventaja de este enfoque es que puede ajustar el Perl para hacer lo que quiera.
zzapper
1
@zzapper: También es eficiente en memoria, ya que cuando lee el archivo línea por línea en lugar de slurp en la memoria como tac.
cuonglm
¿Alguien puede agregar un soporte -m para esto? Me gustaría probar en archivos reales. Ver: gist.githubusercontent.com/ychaouche/…
ychaouche