44 votes

Recherchez "term" et excluez "un autre terme"

Je tente de construire une recherche grep qui cherche un terme mais exclut les lignes qui ont un deuxième terme. Je voulais utiliser plusieurs options -e "motif" mais cela n'a pas fonctionné.

Voici un exemple de commande que j'ai essayée et le message d'erreur qu'elle a généré.

grep -i -E "terme de recherche" -ev "terme à exclure"
grep: terme à exclure: Aucun fichier ou dossier de ce type

Il me semble que le -v s'applique à tous les termes/patrons de recherche. Car cela fonctionne mais ne inclut pas ensuite le terme de recherche dans les résultats.

grep -i -E "terme de recherche" -ve "terme à exclure"

52voto

galeksic Points 121

Pour chercher des expressions avec grep, vous avez besoin de deux invocations :

grep -Ei "terme à rechercher" | grep -Eiv "terme à exclure"

Si les termes que vous recherchez ne sont pas des expressions régulières, utilisez la correspondance de chaîne fixe (-F) qui est plus rapide:

grep -F "terme à rechercher" | grep -Fv "terme à exclure"

25voto

Dennis Points 46916

À court d'appeler grep deux fois, il n'y a qu'une seule façon à laquelle je pense pour y parvenir. Cela implique Perl Compatible Regular Expressions (PCRE) et certaines look-around assertions plutôt astucieuses.

Pour rechercher foo en excluant les correspondances qui contiennent bar, vous pouvez utiliser :

grep -P '(?=^((?!bar).)*$)foo'

Voici comment cela fonctionne :

  • (?!bar) correspond à tout ce qui n'est pas bar sans consommer de caractères de la chaîne. Ensuite, . consomme un seul caractère.

  • ^((?!bar).)* répète ce qui précède depuis le début de la chaîne (^) jusqu'à la fin ($). Cela échouera si bar est rencontré à un moment donné, car (?!bar) ne correspondra pas.

  • (?=^((?!bar).)*$) s'assure que la chaîne correspond au motif précédent, sans consommer de caractères de la chaîne.

  • foo recherche foo comme d'habitude.

J'ai trouvé cette astuce dans Regular expression to match string not containing a word?. Dans la réponse de Bart Kiers, vous pouvez trouver une explication beaucoup plus détaillée du fonctionnement du regard en avant négatif.

18voto

Philip Reese Points 281

Si vous voulez le faire en une seule passe, vous pouvez utiliser awk à la place de grep.

Format:

echo "some text" | awk '/modèle à rechercher/ && !/modèle à exclure/'

Exemples:

  • echo "hello there" | awk '/hello/ && !/there/'

Ne retourne rien.

  • echo "hello thre" | awk '/hello/ && !/there/'

Retourne: hello thre

  • echo "hllo there" | awk '/hello/ && !/there/'

Ne retourne rien.

Pour plusieurs modèles, vous pouvez utiliser des parenthèses pour les regrouper.

Exemples:

  • echo "hello thre" | awk '(/hello/ || /hi/) && !/there/'

Retourne: hello thre

  • echo "hi thre" | awk '(/hello/ || /hi/) && !/there/'

Retourne: hi thre

  • echo "hello there" | awk '(/hello/ || /hi/) && !/there/'

Ne retourne rien.

  • echo "hi there" | awk '(/hello/ || /hi/) && !/there/'

Ne retourne rien.

2voto

nelaaro Points 11661

De mes expériences, il ne semble pas que ça fasse beaucoup de différence si vous filtrez vos termes exclus à travers grep ou sed. Sed a quelques autres fonctionnalités utiles de remplacement de texte que j'utilise souvent pour mieux filtrer la sortie des fichiers journaux. Donc je vais utiliser sed car j'applique un assez grand nombre de filtres sur sed.

wc /var/log/tomcat/tomcat.2013-01-14.log.1
  1851725

 /usr/bin/time grep -i -E "(loginmanager)" /var/log/tomcat/tomcat.2013-01-14.log.1 | sed -e "/login OK/d" -e "/Login expired/d" | wc
24.05user 0.15system 0:25.27elapsed 95%CPU (0avgtext+0avgdata 3504maxresident)k
0inputs+0outputs (0major+246minor)pagefaults 0swaps
   5614   91168 1186298

 /usr/bin/time grep -i -E "(loginmanager)" /var/log/tomcat/tomcat.2013-01-14.log.1 | sed -e "/login OK/d" -e "/Login expired/d" | wc
23.50user 0.16system 0:24.48elapsed 96%CPU (0avgtext+0avgdata 3504maxresident)k
0inputs+0outputs (0major+246minor)pagefaults 0swaps
   5614   91168 1186298

 /usr/bin/time grep -i -E "(loginmanager)" /var/log/tomcat/tomcat.2013-01-14.log.1 | grep -v -e "login OK" -e "Login expired" | wc
23.08user 0.14system 0:23.55elapsed 98%CPU (0avgtext+0avgdata 3504maxresident)k
0inputs+0outputs (0major+246minor)pagefaults 0swaps
   5614   91168 1186298

 /usr/bin/time grep -i -E "(loginmanager)" /var/log/tomcat/tomcat.2013-01-14.log.1 | grep -v -e "login OK" -e "Login expired" | wc
23.50user 0.15system 0:25.27elapsed 93%CPU (0avgtext+0avgdata 3488maxresident)k
0inputs+0outputs (0major+245minor)pagefaults 0swaps
   5614   91168 1186298

1voto

Kapil Points 11

L'option -v (ou --invert-match) filtre les correspondances.

Vous pouvez utiliser grep avec pipe pour chercher un terme et exclure un autre.

grep motif1 *.txt | grep -v motif2

Correspond à toutes les lignes des fichiers "*.txt" contenant motif1, mais pas motif2.

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X