Optimisation de grep GNU

8

J'utilise egrep ( grep -E) avec un fichier PATTERN. ( -f path/to/file).

Cela se fait dans une boucle infinie sur un flux de texte. Cela implique que je ne peux pas accumuler et passer TOUTES les entrées à grep à la fois (comme *.log).

Existe-t-il un moyen de faire grep "enregistrer" le NFA qu'il construit à partir du fichier PATTERN pour l'utiliser pour sa prochaine exécution?

J'ai cherché sur Google et j'ai lu la documentation sans succès.

Je vais essayer de l'expliquer un peu plus. J'ai besoin de localiser un nombre fixe de chaînes avec des expressions rationnelles (cela ne fait pas partie d'une question mais n'hésitez pas à suggérer le contraire) telles que les adresses IP, les domaines, etc. La recherche se fait sur un flux provenant d'Internet. Vous pouvez le considérer comme un flux de texte. Je ne peux pas utiliser grepsur toutes les entrées car c'est un flux. Je peux accumuler un morceau de flux et l'utiliser grepdessus (donc ne pas utiliser grepsur chaque ligne) mais cela est également limité (disons pendant 30 secondes).

Je sais grepest en train de construire un NFA à partir de tous ses modèles (dans mon cas à partir d'un fichier). Donc ma question ici est: puis-je dire grepde sauvegarder ce NFA pour la prochaine exécution, car il ne va pas changer? Cela me ferait gagner du temps pour construire cette NFA à chaque fois.

bergerg
la source
Qu'entendez-vous par Cela se fait dans une boucle infinie sur un flux de texte ? Voulez-vous dire que vous en exécutez un greppar ligne de texte? D'où vient le texte? Serait tail -fune option?
Stéphane Chazelas
Disons que j'accumule le flux pendant 30 secondes et que je cours grepsur ce morceau.
bergerg
1
On ne sait toujours pas pourquoi vous devez exécuter grepplusieurs fois. Peut-être lié: pourquoi la mise en correspondance de 1250 chaînes avec des motifs de 90k est-elle si lente?
Stéphane Chazelas
5
grepest censé fonctionner sur un flux de texte, je ne comprends toujours pas pourquoi vous devez exécuter plusieurs instances. Pourquoi ne pouvez-vous pas alimenter tous ces éléments dans la même grepinstance? Pourquoi avez-vous besoin de les accumuler avant de les nourrir grep?
Stéphane Chazelas
2
Jetez un oeil à flex et écrivez votre propre programme, qui peut s'avérer beaucoup plus rapide.
user2064000

Réponses:

14

Non, il n'y a rien de tel. Généralement, le coût de démarrage grep( lancer un nouveau processus, charger l'exécutable, bibliothèque partagée, liaison dynamique ...) serait beaucoup plus élevé que la compilation des regexps, donc ce type d'optimisation n'aurait aucun sens.

Bien voir pourquoi Pourquoi la mise en correspondance de 1250 chaînes contre des motifs de 90k est-elle si lente? à propos d'un bug dans certaines versions de GNU grepqui le rendrait particulièrement lent pour un grand nombre d'expressions régulières.

Ici, vous pouvez peut-être éviter d'exécuter grepplusieurs fois en alimentant vos morceaux dans la même grepinstance, par exemple en l'utilisant comme un co-processus et en utilisant un marqueur pour détecter la fin. Avec zshet GNU grepet awkimplémentations autres que mawk:

coproc grep -E -f patterns -e '^@@MARKER@@$' --line-buffered
process_chunk() {
  { cat; echo @@MARKER@@; } >&p & awk '$0 == "@@MARKER@@"{exit};1' <&p
}
process_chunk < chunk1 > chunk1.grepped
process_chunk < chunk2 > chunk2.grepped

Bien qu'il soit plus simple de faire le tout avec awkou à la perlplace.

Mais si vous n'avez pas besoin de la grepsortie pour entrer dans différents fichiers pour différents morceaux, vous pouvez toujours faire:

{
  cat chunk1
  while wget -qO- ...; done # or whatever you use to fetch those chunks
  ...
} | grep -Ef patterns > output
Stéphane Chazelas
la source
J'ai veraion 3+ de grep donc ce n'est pas le problème. Je n'ai même pas considéré les frais généraux de la fourche. Je suppose que je vais essayer de tout diffuser greptel quel. Merci.
bergerg
L'exécutable et les bibliothèques partagées ne resteraient-ils pas dans les tampons RAM après la fin des processus (à moins que l'OP ne soit réellement faible en RAM)?
Dmitry Grigoryev
2
@DmitryGrigoryev, oui, très probablement, doit encore être mappé dans l'espace d'adressage du processus et faire la modification du lien. Il y a plus comme charger et analyser les données locales, analyser les options, l'environnement ... Le fait est que le coût de regcomp () est dilué dans tous ces frais généraux. La première chose à faire lors de l'optimisation serait d'éviter d'exécuter plusieurs greps en premier lieu.
Stéphane Chazelas
1

Je ne peux pas utiliser grep sur toutes les entrées car c'est un flux. Je peux accumuler un morceau de flux et utiliser grep dessus ...

Savez-vous que les pipelines se bloquent? Si vous dirigez quelque chose vers grep et que toutes les entrées ne sont pas disponibles, grep attendra qu'il soit disponible, puis continuera comme si l'entrée était là tout le temps.

$ ( echo a1; echo b1; sleep 5; echo a2 ) | grep 'a.'
a1
a2

EDIT: Le fonctionnement des pipelines, par exemple avec, cmd1 | cmd2est que les deux programmes démarreront en même temps, avec par exemple un "tampon de bloc" de 65 536 octets entre eux. Lorsque cmd2tente de lire et que le tampon est vide, il attendra qu'un morceau soit disponible. Quand cmd1essaie d'écrire et que le tampon est plein, il attendra jusqu'à cmd2ce qu'il le lise.

D'après ce que je peux lire, il n'est pas nécessaire de couper l'entrée en morceaux et de les passer à grep séparément. C'est déjà fait automatiquement.

EDIT2: grepdevrait également imprimer les résultats dès qu'il les trouve dans le flux. Il n'est pas nécessaire que le flux se termine avant d'obtenir vos résultats.

JoL
la source
0

Vous pouvez peut-être "utiliser grep sur toutes les entrées"? Vous ncutilisez (netcat), ou via scriptou via d'autres outils similaires? Surtout si votre fichier de signatures est de taille gérable (disons moins de 1000 regexps).

Premier exemple : vous pouvez egrepune connexion en streaming: (ici l'exemple montré avec nc, mais d'autres pourraient s'appliquer)

prompt:/some/path $ nc somehost someport | egrep -f patternfile | gzip -c - > results.gz

# and while this is running, you can have a look at the growing results.gz:
prompt:/some/otherpath $ tail -f /some/path/results.gz | gzip -c - | less

(note: vous pouvez même: touch /some/path/results.gzavant de lancer la nccommande, et avoir tail -fsur ce fichier (vide) pour ne rien rater. Quoi qu'il en soit, le results.gz contiendra tout ce que vous vouliez attraper)

deuxième exemple : Vous pourriez même egrepsur une session shell en cours d'exécution (et montrant une autre façon de suivre la progression):

#in 1 terminal:
prompt:/home/userA $ script
Script command is started. The file is typescript.
prompt:/home/userA $ 
 ... doing here whatever you want (start IRC? etc) ...
prompt:/home/userA $ ctrl-d # to end the current script session
Script command is complete. The file is typescript.

#and in another terminal, while you are "doing here whatever you want" :
prompt:/home/somewhere $ tail -f /home/userA/typescript | egrep -f patternfile  | tee /some/place/to/store/results.gz

egrepest une version très efficace de grep, sur la plupart des systèmes (voir quelques infos intéressantes sur: https://swtch.com/~rsc/regexp/regexp1.html )

Olivier Dulac
la source
vous pourriez même utiliser exemple1 sur des choses comme une sortie dd, etc.
Olivier Dulac
note intéressante: grep est plus efficace plus la partie connue de l'expression rationnelle est grande (ex: la recherche de la chaîne ou l'expression régulière sest beaucoup, bouillie plus lente que la correspondance somethinget cela est beaucoup plus lent que la correspondance something even much longer(cette dernière permettant à la correspondance d'expression régulière de sauter plus grande) sur des fichiers volumineux, il "divise" essentiellement le temps de l'analyser par le rapport de longueur (c'est-à-dire que le fait de saluer 1 caractère connu est presque 40 fois plus lent que de correspondre à une chaîne de 40 caractères connus. Je n'ai pas t prof mais c'est vraiment perceptible.)
Olivier Dulac