grep pour "terme" et exclure "un autre terme"

28

J'essaie de construire une recherche grep qui recherche un terme mais exclut les lignes qui ont un deuxième terme. Je voulais utiliser plusieurs -e "pattern"options 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 "search term" -ev "exclude term"
grep: exclude term: No such file or directory

Il me semble que le -vs'applique à tous les termes / modèles de recherche. Comme cela s'exécute, mais ne comprend pas search termdans les résultats.

grep -i -E "search term" -ve "exclude term"
nelaaro
la source
Existe-t-il une autre option pour exclure, car parfois nous devons grep les lignes autour d'un mot et si nous excluons dans la prochaine opération en utilisant '|' , il supprime simplement ce mot mais ne supprime pas le bloc pour ce mot
Apprenant

Réponses:

40

Pour et les expressions avec grep, vous avez besoin de deux invocations:

grep -Ei "search term" | grep -Eiv "exclude term"

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

grep -F "search term" | grep -Fv "exclude term"
Thor
la source
18

À moins d'invoquer grep deux fois, il n'y a qu'une seule façon de penser à cela. Il s'agit d' expressions régulières compatibles Perl (PCRE) et de quelques assertions de regard plutôt hacky .

Pour rechercher foo en excluant les correspondances contenant une barre , vous pouvez utiliser:

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

Voici comment cela fonctionne:

  • (?!bar)correspond à tout ce qui ne barre pas sans consommer les caractères de la chaîne. .Consomme ensuite un seul personnage.

  • ^((?!bar).)*répète ce qui précède du début de la chaîne ( ^) à la fin de celle-ci ( $). Il échouera s'il barest rencontré à un moment donné, car (?!bar)ne correspondra pas.

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

  • foorecherche foo comme d'habitude.

J'ai trouvé ce hack dans l' expression régulière pour faire correspondre une chaîne ne contenant pas de mot? . Dans la réponse de Bart Kiers , vous pouvez trouver une explication beaucoup plus détaillée du fonctionnement de l' anticipation négative.

Dennis
la source
Joli hack. Cette astuce fonctionne aussi en Java, btw.
Raman
12

Si vous voulez faire cela en un seul passage, vous pouvez utiliser awk au lieu de grep.

Format:

echo "some text" | awk '/pattern to match/ && !/pattern to exclude/'

Exemples:

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

Ne renvoie rien.

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

Retours: bonjour trois

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

Ne renvoie rien.

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

Exemples:

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

Retours: bonjour trois

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

Retours: salut trois

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

Ne renvoie rien.

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

Ne renvoie rien.

Philip Reese
la source
1
Cela a fonctionné pour moi, mais j'ai perdu les couleurs = P
Leopoldo Sanczyk
1
Couleurs de quelle sortie? Si vous essayez de conserver les couleurs avec ls, utilisez l'argument "--color = always" chaque fois que vous analysez la sortie (ou vous perdrez normalement toujours les couleurs lors de l'analyse du texte). Exemple: ls --color=always | awk '/hello/ && !/goodbye/'
Philip Reese
Merci pour la réponse @Philip! J'ai essayé ça avant, mais sans succès. Je suppose que comme le motif a le texte en couleur, il ne correspond pas plus tard, et je devrais inclure une sorte de code couleur dans le motif. Quoi qu'il en soit, le vôtre est le moyen le plus rapide que j'ai trouvé à faire grep -Rdans plusieurs fichiers de code en utilisant la ligne de commande Ubuntu.
Leopoldo Sanczyk
1

D'après mes expériences, cela ne fait pas beaucoup de différence si vous dirigez vos termes d'exclusion via grepou sed. Sed a d'autres fonctionnalités utiles de remplacement de texte que j'utilise souvent pour mieux filtrer la sortie des fichiers journaux. Je vais donc utiliser sed car je combine un certain nombre de filtres sur sed.

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

 / usr / bin / time grep -i -E "(gestionnaire de connexion)" /var/log/tomcat/tomcat.2013-01-14.log.1 | sed -e "/ connexion OK / d" -e "/ Connexion expirée / d" | toilettes
24.05utilisateur 0.15system 0: 25.27 CPU 95% écoulé (0avgtext + 0avgdata 3504maxresident) k
0 entrées + 0 sorties (0major + 246minor) pagefaults 0swaps
   5614 91168 1186298

 / usr / bin / time grep -i -E "(gestionnaire de connexion)" /var/log/tomcat/tomcat.2013-01-14.log.1 | sed -e "/ connexion OK / d" -e "/ Connexion expirée / d" | toilettes
23,50 utilisateur 0,16 système 0: 24,48% du processeur 96% (0avgtext + 0avgdata 3504maxresident) k
0 entrées + 0 sorties (0major + 246minor) pagefaults 0swaps
   5614 91168 1186298

 / usr / bin / time grep -i -E "(gestionnaire de connexion)" /var/log/tomcat/tomcat.2013-01-14.log.1 | grep -v -e "connexion OK" -e "Connexion expirée" | toilettes
23.08utilisateur 0.14system 0: 23.55 CPU 98% écoulé (0avgtext + 0avgdata 3504maxresident) k
0 entrées + 0 sorties (0major + 246minor) pagefaults 0swaps
   5614 91168 1186298

 / usr / bin / time grep -i -E "(gestionnaire de connexion)" /var/log/tomcat/tomcat.2013-01-14.log.1 | grep -v -e "connexion OK" -e "Connexion expirée" | toilettes
23.50user 0.15system 0: 25.27 CPU 93% écoulé (0avgtext + 0avgdata 3488maxresident) k
0 entrées + 0 sorties (0major + 245minor) pagefaults 0swaps
   5614 91168 1186298

nelaaro
la source
3
Essayez de comparer le temps d'exécution de grep -Fau lieu de grep -Eet ne l'utilisez pas -isi vous n'en avez pas besoin.
Thor
1
Mais alors vous ne fournissez pas d'exemples en utilisant sed;)
Benjamin R