Grep : montre les lignes avec toutes les chaînes présentes

Bonjour,
on considère un fichier ayant de multiples occurences de chaîne1 et de chaîne2, mais une seule ligne ayant chaîne1 et chaîne2, comment la montrer avec un seul grep ?

Bon, j’ai trouvé :$ grep chaîne1'.*'chaîne2 fichier
Faudra vraiment que je mette aux expressions régulières un jour …

tu veux chaine1 avant chaine2 ou les deux sens ?

21:38:30 stepharch@toshi:~ 126$ echo "chaine1 gchaine1g chaine2s fchaine2 chaine1 y chaine2d chaine2 g schaine1"|grep -E '(chaine2.*chaine1)|(chaine1.*chaine2)' gchaine1g chaine2s chaine1 y chaine2d chaine2 g schaine1 21:41:05 stepharch@toshi:~ 127$ echo "chaine1 gchaine1g chaine2s fchaine2 chaine1 y chaine2d chaine2 g schaine1"|grep -E 'chaine1.*chaine2' gchaine1g chaine2s chaine1 y chaine2d 21:41:28 stepharch@toshi:~ 128$

[quote=“kamui57”]tu veux chaine1 avant chaine2 ou les deux sens ?[/quote]Uniquement chaîne1 avant chaïne2.

La question est intéressante et le fil certainement instructif et, j’espère, loin d’être terminé.
Appel aux connaisseurs et puristes, en commençant par une provocation: pourquoi pas 2 grep ?
Sur le plan pratique, si le texte est très long et les occurences nombreuses, 2 “grep” seront peut-être plus efficaces:

plutot que

( à tester avec time, au minimum )

Le 1er grep ellimine tout ce qui ne contient ‘pas chaine 1’ et le 2e grep a donc beucoup moins de travail ( sauf si la pluspart des lignes contient les 2 chaines )
En outre la recherche de ce qui n’est pas une chaine explicite ( le point: . ) est un plus gros travail pour grep.

Il semble aussi que egrep soit optimisé par rapport à grep -E ( d’après une vielle doc, j’ai oublié laquelle )

[quote=“josephtux”]en commençant par une provocation: pourquoi pas 2 grep ?
Sur le plan pratique, si le texte est très long et les occurences nombreuses, 2 “grep” seront peut-être plus efficaces:

Plus que l’usage de deux grep, je dirais que la provocation se situe dans l’UUOC :slightly_smiling:

voici mes tests, faux à mon avis : grep va plus vite à la fin qu’au début (la première commande est la plus lente de loin) alors comment tester sans fausser les résultats ?

[code]23:40:37 stepharch@toshi:~ 11$ time cat /var/log/Xorg.0.log | egrep ‘EE’ | egrep ‘II’
[ 25.348] (II) Loading extension MIT-SCREEN-SAVER

real 0m0.016s
user 0m0.007s
sys 0m0.000s
23:41:08 stepharch@toshi:~ 12$ egrep /var/log/Xorg.0.log 'EE.*II|II.*EE’egrep: EE.*II|II.*EE: Aucun fichier ou dossier de ce type
23:42:05 stepharch@toshi:~ 13$ egrep ‘EE.*II|II.*EE’ /var/log/Xorg.0.log
[ 25.348] (II) Loading extension MIT-SCREEN-SAVER
23:42:18 stepharch@toshi:~ 14$ time egrep ‘EE.*II|II.*EE’ /var/log/Xorg.0.log
[ 25.348] (II) Loading extension MIT-SCREEN-SAVER

real 0m0.007s
user 0m0.003s
sys 0m0.000s
23:42:23 stepharch@toshi:~ 15$ cat /var/log/Xorg.0.log | egrep ‘EE’ | egrep ‘II’
[ 25.348] (II) Loading extension MIT-SCREEN-SAVER
23:42:34 stepharch@toshi:~ 16$ time cat /var/log/Xorg.0.log | egrep ‘EE’ | egrep ‘II’
[ 25.348] (II) Loading extension MIT-SCREEN-SAVER

real 0m0.005s
user 0m0.000s
sys 0m0.007s
23:42:40 stepharch@toshi:~ 17$ time egrep ‘EE.*II|II.*EE’ /var/log/Xorg.0.log[ 25.348] (II) Loading extension MIT-SCREEN-SAVER

real 0m0.004s
user 0m0.000s
sys 0m0.003s
23:42:48 stepharch@toshi:~ 18$ time cat /var/log/Xorg.0.log | egrep ‘EE’ | egrep ‘II’
[ 25.348] (II) Loading extension MIT-SCREEN-SAVER

real 0m0.005s
user 0m0.007s
sys 0m0.003s
23:42:57 stepharch@toshi:~ 19$ time egrep ‘EE.*II|II.*EE’ /var/log/Xorg.0.log[ 25.348] (II) Loading extension MIT-SCREEN-SAVER

real 0m0.004s
user 0m0.000s
sys 0m0.000s
23:42:58 stepharch@toshi:~ 20$ time cat /var/log/Xorg.0.log | egrep ‘EE’ < /var/lo| egrep 'II’
local/ lock/ log/
23:42:58 stepharch@toshi:~ 20$ time cat /var/log/Xorg.0.log | egrep ‘EE’ < /var/lo| egrep 'II’
local/ lock/ log/
23:42:58 stepharch@toshi:~ 20$ egrep ‘EE’ < /var/log/Xorg.0.log| egrep ‘II’[ 25.348] (II) Loading extension MIT-SCREEN-SAVER
23:43:24 stepharch@toshi:~ 21$ time egrep ‘EE’ < /var/log/Xorg.0.log| egrep ‘II’
[ 25.348] (II) Loading extension MIT-SCREEN-SAVER

real 0m0.006s
user 0m0.007s
sys 0m0.000s
23:43:27 stepharch@toshi:~ 22$ time egrep ‘EE.*II|II.*EE’ /var/log/Xorg.0.log[ 25.348] (II) Loading extension MIT-SCREEN-SAVER

real 0m0.004s
user 0m0.000s
sys 0m0.007s
23:43:31 stepharch@toshi:~ 23$ time cat /var/log/Xorg.0.log | egrep ‘EE’ | egrep ‘II’
[ 25.348] (II) Loading extension MIT-SCREEN-SAVER

real 0m0.006s
user 0m0.003s
sys 0m0.003s
23:43:39 stepharch@toshi:~ 24$
[/code]

Pour un benchmark précis, plusieurs choses à prendre en compte généralement :

  1. une machine la plus stable possible au niveau charge (CPU, disques etc) – ça veut dire fermer X, et tous les services qui ne sont pas absolument nécessaires (Apache, MySql, SSH, NTP, etc), le but étant d’éviter au maximum les interférences dues à d’autres programmes ; si le processeur réduit sa fréquence lorsqu’il est inutilisé, il faut désactiver temporairement cette option et le pousser à fond (cf. cpufreq et compagnie)
  2. la première exécution est plus lente car certaines infos ne sont pas encore en cache, il faut donc lancer la commande une première fois (voire plus, ça mange pas de pain)
  3. toujours lancer la commande dans une boucle assez importante pour “lisser” les résultats, et mesurer le total (1000 à 10000 fois me paraît le strict minimum pour un temps d’exécution de ~5ms ; plus ta commande est rapide à exécuter plus il faut augmenter la boucle)
  4. si cela est possible, éliminer la sortie de la commande sur le terminal, ça évite des aléas dûs à l’affichage (par contre il semblerait que ça aie tendance à ralentir un poil la commande, ce qui n’est pas très important quand tu veux comparer les vitesses relatives de plusieurs commandes équivalentes)
  5. lancer le benchmark plusieurs fois d’affilée pour estimer la marge d’erreur ; si elle est trop importante, une seule solution pour la réduire : augmenter la taille de la boucle mesurée (la variation absolue reste à peu près constante, mais diminue proportionnellement au total), voire la taille de la boucle initiale si tu enchaînes plusieurs tests différents et qu’il y a vraiment besoin de “chauffer” le cache.

[code]#!/bin/bash

boucle initiale pour “chauffer” le cache

TIMES_WARMUP=100

boucle mesurée : on prend l’argument de la ligne de commandes

TIMES_BENCH=$1

function loopX()
{
local TIMES=$1
shift
for ((i=1 ; i<=$TIMES; i++)); do
"$@" &>/dev/null
done
}

utilisation:

bench_cmd COMMANDE ARGUMENTS…

function bench_cmd()
{
loopX $TIMES_WARMUP "$@"
time loopX $TIMES_BENCH “$@”
}

echo "cat /var/log/Xorg.0.log | egrep ‘EE’ | egrep ‘II’"
bench_cmd cat /var/log/Xorg.0.log | egrep ‘EE’ | egrep 'II’
echo
echo "egrep ‘EE.*II|II.*EE’ /var/log/Xorg.0.log"
bench_cmd egrep ‘EE.*II|II.*EE’ /var/log/Xorg.0.log[/code]

Sachant que je n’ai pas appliqué le point (1) mes résultats varient de presque 200ms d’un lancement à l’autre, mais l’ordre de grandeur (4.3 secondes, soit ~33%) est tout de même parlant je pense (10000 itérations) :

[code]$ ./bench 10000
cat /var/log/Xorg.0.log | egrep ‘EE’ | egrep ‘II’

real 0m12.733s
user 0m0.976s
sys 0m2.300s

egrep ‘EE.*II|II.*EE’ /var/log/Xorg.0.log

real 0m17.004s
user 0m1.072s
sys 0m2.340s[/code]

Avec 100000 itérations dans les mêmes conditions, l’ordre de grandeur reste le même (et environ 400ms de variation, ce qui signifie une marge d’erreur réduite par 5 par rapport à 10000 itérations) :

[code]$ ./bench 100000
cat /var/log/Xorg.0.log | egrep ‘EE’ | egrep ‘II’

real 2m15.969s
user 0m42.191s
sys 0m20.781s

egrep ‘EE.*II|II.*EE’ /var/log/Xorg.0.log

real 2m59.612s
user 0m42.439s
sys 0m21.737s[/code]

Bref, josephtux semble avoir eu raison. :smiley:

Et dans le cas de chaîne1 avant chaîne2 uniquement ?
Donc avec :

$ egrep EE'.*'II /var/log/Xorg.0.logau lieu de

Tant qu’à faire, autant tester fgrep aussi, qui recherche une chaîne fixe plutôt qu’une regex, et devrait (?) être plus rapide :

[code]$ ./bench 10000
cat /var/log/Xorg.0.log | egrep ‘EE’ | egrep ‘II’

real 0m12.679s
user 0m0.920s
sys 0m2.304s

cat /var/log/Xorg.0.log | fgrep ‘EE’ | fgrep ‘II’

real 0m12.933s
user 0m1.040s
sys 0m2.360s

egrep ‘EE.*II|II.*EE’ /var/log/Xorg.0.log

real 0m16.987s
user 0m1.048s
sys 0m2.352s

egrep ‘EE.*II’ /var/log/Xorg.0.log

real 0m16.302s
user 0m1.076s
sys 0m2.388s

$ ./bench 100000
cat /var/log/Xorg.0.log | egrep ‘EE’ | egrep ‘II’

real 2m15.588s
user 0m34.426s
sys 0m27.042s

cat /var/log/Xorg.0.log | fgrep ‘EE’ | fgrep ‘II’

real 2m17.942s
user 0m38.702s
sys 0m24.022s

egrep ‘EE.*II|II.*EE’ /var/log/Xorg.0.log

real 2m59.320s
user 0m40.207s
sys 0m23.101s

egrep ‘EE.*II’ /var/log/Xorg.0.log

real 2m51.798s
user 0m47.523s
sys 0m23.093s[/code]

Après test, il semblerait que contrairement à mes attentes fgrep ‘EE’ | fgrep ‘II’ soit un poil plus lent que egrep ‘EE’ | egrep ‘II’, mais j’aurais tendance à dire que la différence n’est pas vraiment significative.
De même, egrep ‘EE.*II’ (chaînes dans l’ordre) est un tout petit peu plus rapide que egrep ‘EE.*II|II.*EE’ (chaînes dans le désordre) sans que cela fasse une grosse différence, ça reste dans le même ordre de grandeur et toujours beaucoup plus lent qu’un egrep ‘EE’ | egrep ‘II’.

J’aimerais bien tester ça un peu plus en profondeur mais je n’aurais probablement pas le temps d’ici vendredi. Par contre pour filtrer des lignes de fichiers il y a encore d’autres méthodes.
Awk
juste une chaine simple :

filtrer en fonction de deux chaines dans le désordre :

Devrait être plus lent.

Sed
juste une chaine simple :

filtrer en fonction de deux chaines dans le désordre :

Ça devrait être intéressant parce que sed est un outil très rapide.

Perl
juste une chaine simple :

ou

filtrer en fonction de deux chaines dans le désordre :

ou

Normalement la version avec say devrait être plus longue que la version print, mais de toute manière perl devrait être plus lent que les autres (il entre plutôt en concurrence avec awk.

Juste par curiosité j’ai refait quelques tests.
Il s’agit exactement des mêmes commandes que mon test précédent, mais en utilisant systématiquement les deux constructions cat fichier | grep motif et grep motif fichier.

Les résultats sont très intéressants, ils semblent prouver que la plus grosse partie de la différence tient à l’utilisation (ou non) du cat et non pas aux expressions régulières / pipes.

[code]$ ./bench 10000
cat /var/log/Xorg.0.log | egrep ‘EE’ | egrep ‘II’

real 0m12.525s
user 0m0.984s
sys 0m2.220s

egrep ‘EE’ /var/log/Xorg.0.log | egrep ‘II’

real 0m15.219s
user 0m1.020s
sys 0m2.236s

cat /var/log/Xorg.0.log | fgrep ‘EE’ | fgrep ‘II’

real 0m12.687s
user 0m0.984s
sys 0m2.280s

fgrep ‘EE’ /var/log/Xorg.0.log | fgrep ‘II’

real 0m15.823s
user 0m0.924s
sys 0m2.372s

cat /var/log/Xorg.0.log | egrep ‘EE.*II|II.*EE’

real 0m12.689s
user 0m0.960s
sys 0m2.256s

egrep ‘EE.*II|II.*EE’ /var/log/Xorg.0.log

real 0m16.961s
user 0m1.240s
sys 0m2.148s

cat /var/log/Xorg.0.log | egrep ‘EE.*II’

real 0m12.772s
user 0m1.024s
sys 0m2.332s

egrep ‘EE.*II’ /var/log/Xorg.0.log

real 0m16.240s
user 0m1.076s
sys 0m2.380s[/code]

Effectivement, l’affichage par cat est du gaspillage ! ( j’avais oublié d’en discuter avec mon tux en peluche )
je propose, pour garder la même logique:

Non justement ! Si on se fie à mes mesures, l’utilisation de cat | grep est plus rapide que grep tout seul.
Regarde les résultats dans mon précédent message, tu verras, la version avec cat est systématiquement plus rapide que son équivalent sans cat. :wink:
Et accessoirement, il n’y a pas vraiment de différence entre les diverses versions qui utilisent cat (que ce soit plusieurs grep en pipe ou un seul grep avec une expression régulière).

Je sais que ça paraît contre-intuitif (j’ai été le premier surpris) mais en y réfléchissant il est fort possible que ça soit dû au fait que le pipe utilise des tampons, ce qui accélère le tout.

En effet, à une lecture trop rapide des tests s’ajoute une nouvelle erreur de logique de ma part: ce qui semble couteux ce n’est pas cat, mais l’affichage de sa sortie; avec un [ tube | pipe ], il n’y en a pas !

ya aussi # grep for AAA and BBB and CCC (in any order) sed '/AAA/!d; /BBB/!d; /CCC/!d'
trouvé ici par hasard