Grepping inversé

44

Disons que j'ai un très gros fichier texte (environ 10.000.000 lignes). J'en ai besoin à greppartir de la fin et enregistrer le résultat dans un fichier. Quel est le moyen le plus efficace d'accomplir cette tâche?

le chaos
la source
10
Utilisez tacet greppour réaliser ce que vous voulez.
Valentin Bajrami
1
En plus des excellentes solutions publiées, GNU grepdispose d’un --max-count (number)commutateur qui abandonne après un certain nombre de correspondances, ce qui pourrait vous intéresser.
Ulrich Schwarz
@ val0x00ff pourriez-vous jeter un oeil à cette question
c0rp
Savez-vous combien de fois vous allez avoir? Lorsque vous pensez que votre grep trouvera 3 lignes, démarrez grepping et inversez-le par la suite.
Walter A

Réponses:

46

tac / grep Solution

tac file | grep whatever

Ou un peu plus efficace:

grep whatever < <(tac file)

Heure avec un fichier de 500 Mo:

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

Solution sed / grep :

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

Heure avec un fichier de 500 Mo: Abandonné après 10 minutes ou plus.

awk / grep Solution:

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

Heure avec un fichier de 500 Mo:

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

Solution perl / grep :

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

Heure avec un fichier de 500 Mo:

real    0m3.551s
user    0m3.104s
sys     0m1.036s
le chaos
la source
2
sed, awket perl(avec cette méthode) ne sont pas acceptables car ils lisent le fichier depuis le début, ce qui est très inefficace. Je suppose que cela tacfait la bonne chose.
vinc17
1
@ vinc17 Oui, les statistiques temporelles indiquent ce que vous avez dit.
Chaos
2
@ val0x00ff Le < <(tac filename)devrait être aussi rapide qu'un tuyau: dans les deux cas, les commandes sont exécutées en parallèle.
vinc17
7
Si vous visez l'efficacité, il serait préférable de mettre tacaprès le grep. Si vous avez un fichier de 10 000 000 lignes, avec seulement 2 correspondances, vous tacne devrez inverser que 2 lignes et non 10 m. grepva encore devoir passer par la chose entière de toute façon.
Patrick
3
Si vous mettez tacaprès le grep, il lira un tuyau et ne pourra donc pas chercher. Cela le rendra moins efficace (ou échouera complètement) si le nombre de lignes trouvées est important.
jjanes
17

Cette solution pourrait aider:

tac file_name | grep -e expression
Anveshak
la source
3
tacest la commande GNU. Sur la plupart des autres systèmes, l'équivalent est tail -r.
Stéphane Chazelas
@ Stéphane: Sur certains systèmes Unix au moins, tail -rse limite à un petit nombre de lignes, cela peut poser problème.
RedGrittyBrick
1
@RedGrittyBrick, avez-vous une référence à ce sujet ou pouvez-vous indiquer quels systèmes ont cette limitation?
Stéphane Chazelas
@ StéphaneChazelas, tail -r /etc/passwdéchoue avec tail: invalid option -- 'r'. J'utilise coreutils-8.21-21.fc20.x86_64.
Cristian Ciupitu
@CristianCiupitu, comme je l'ai dit, GNU a tac(et seul GNU a tac) beaucoup d'autres Unices tail -r. GNU tailne supporte pas-r
Stéphane Chazelas
10

Celui-ci sort dès qu'il trouve le premier match:

 tac hugeproduction.log | grep -m1 WhatImLookingFor

Ce qui suit donne les 5 lignes avant et après les deux premiers matches:

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

Rappelez-vous de ne pas utiliser -i(insensible à la casse) à moins que vous n'ayez à le faire car cela ralentirait le grep.

Si vous connaissez la chaîne exacte que vous recherchez, envisagez fgrep(chaîne fixe)

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

Si le fichier est vraiment gros, ne peut pas tenir dans la mémoire, je vais utiliser Perlavec le module File :: ReadBackwards 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;

Ensuite:

$ ./reverse-grep.pl pattern file
cuonglm
la source
L'avantage de cette approche est que vous pouvez adapter Perl à tout ce que vous voulez.
zzapper
1
@zzapper: La mémoire est efficace aussi, puisqu'elle lit le fichier ligne par ligne au lieu du fichier slurp en mémoire tac.
jeudi
quelqu'un peut-il ajouter un support -m pour cela? Je voudrais tester sur de vrais fichiers. Voir: gist.githubusercontent.com/ychaouche/…
ychaouche