Comment puis-je grep pour plusieurs motifs avec des motifs ayant un caractère pipe?


624

Je veux trouver toutes les lignes dans plusieurs fichiers qui correspondent à l'un des deux modèles. J'ai essayé de trouver les motifs que je cherchais en tapant

grep (foo|bar) *.txt

mais le shell interprète le |comme un tuyau et se plaint quand ce barn'est pas un exécutable.

Comment puis-je grep pour plusieurs modèles dans le même ensemble de fichiers?



grep 'word1 \ | word2 \ | word3' / chemin / vers / fichier
lambodar

Réponses:


861

Tout d'abord, vous devez protéger le motif de l'expansion par le shell. Le moyen le plus simple de le faire est de mettre des guillemets simples autour. Les guillemets simples empêchent toute expansion entre eux (y compris les barres obliques inverses); la seule chose que vous ne puissiez pas faire est d'avoir des guillemets simples dans le motif.

grep 'foo*' *.txt

Si vous avez besoin d'un seul guillemet, vous pouvez l'écrire comme '\''suit (littéral de chaîne de fin, citation littérale, littéral de chaîne ouverte).

grep 'foo*'\''bar' *.txt

Deuxièmement, grep prend en charge deux syntaxes pour les modèles. L'ancienne syntaxe par défaut ( expressions régulières de base ) ne prend pas en charge l' |opérateur alternance ( ), bien que certaines versions l'aient comme extension, mais elle est écrite avec une barre oblique inverse.

grep 'foo\|bar' *.txt

La méthode portable consiste à utiliser la syntaxe la plus récente, les expressions régulières étendues . Vous devez passer l' -Eoption pour greple sélectionner. Sous Linux, vous pouvez également taper à la egrepplace de grep -E(sur d’autres serveurs, vous pouvez en faire un alias).

grep -E 'foo|bar' *.txt

Une autre possibilité lorsque vous recherchez simplement plusieurs modèles (par opposition à la création d'un modèle complexe utilisant la disjonction) consiste à transmettre plusieurs modèles à grep. Vous pouvez le faire en précédant chaque motif avec l' -eoption.

grep -e foo -e bar *.txt

18
Sidenote - lorsque les motifs sont corrigés, vous devriez vraiment prendre l'habitude de, fgrepou grep -F, pour les petits motifs, la différence sera négligeable, mais à mesure qu'ils s'allongent, les avantages commencent à se manifester ...
TC1

7
@ TC1 fgrep est déconseillé selon la page de
manuel

18
@ TC1 Le grep -Fbénéfice réel en termes de performances dépend de l'implémentation de grep: certains d'entre eux appliquent quand même le même algorithme, de sorte que cela ne -Ffait de différence que pour le temps passé à analyser le motif et non pour le temps de recherche. GNU grep n’est pas plus rapide avec -F, par exemple (il a aussi un bogue qui grep -Fralentit les locales multi-octets - le même motif constant avec grepest en réalité nettement plus rapide!). D'autre part, BusyBox grep tire profit -Fdes fichiers volumineux.
Gilles

4
Peut-être faudrait-il mentionner que, pour les modèles plus complexes où l'alternance ne concerne qu'une partie de l'expression régulière, elle peut être groupée avec "\ (" et "\)" (l'échappement correspond au paramètre par défaut "expressions régulières de base" ) (?).
Peter Mortensen

4
Notez que egrepest antérieure à grep -E. Ce n'est pas spécifique à GNU (cela n'a certainement rien à voir avec Linux). En fait, vous trouverez toujours des systèmes comme Solaris où la configuration par défaut grepne prend toujours pas en charge -E.
Stéphane Chazelas

90
egrep "foo|bar" *.txt

ou

grep "foo\|bar" *.txt
grep -E "foo|bar" *.txt

citant de manière sélective la page de manuel de gnu-grep:

   -E, --extended-regexp
          Interpret PATTERN as an extended regular expression (ERE, see below).  (-E is specified by POSIX.)

Matching Control
   -e PATTERN, --regexp=PATTERN
          Use PATTERN as the pattern.  This can be used to specify multiple search patterns, or to protect  a  pattern
          beginning with a hyphen (-).  (-e is specified by POSIX.)

(...)

   grep understands two different versions of regular expression syntax: basic and extended.”  In  GNU grep,  there
   is  no  difference  in  available  functionality  using  either  syntax.   In  other implementations, basic regular
   expressions are less powerful.  The following description applies to extended regular expressions; differences  for
   basic regular expressions are summarized afterwards.

Au début, je n'ai pas lu plus loin, alors je n'ai pas reconnu les différences subtiles:

Basic vs Extended Regular Expressions
   In basic regular expressions the meta-characters ?, +, {, |, (, and ) lose their special meaning; instead  use  the
   backslashed versions \?, \+, \{, \|, \(, and \).

J'ai toujours utilisé egrep et inutilement parens, parce que j'ai appris des exemples. Maintenant j'ai appris quelque chose de nouveau. :)


22

Comme dit TC1, -Fsemble être une option utilisable:

$> cat text
some text
foo
another text
bar
end of file

$> patterns="foo
bar" 

$> grep -F "${patterns}" text
foo
bar

1
@poige Je ne connaissais pas l'option $ 'foo \ nbar', je ne sais pas comment l'extension fonctionne ici, il faut rechercher, mais merci, c'est vraiment utile.
haridsv

Agréable! Cette option semble également la rendre beaucoup plus rapide (car elle désactive regex).
Qwertzguy

15

Tout d'abord, vous devez utiliser des guillemets pour les caractères spéciaux. Deuxièmement, même ainsi, grepne comprendra pas directement l’alternance; vous devrez utiliser egrep, ou (avec GNU grepuniquement) grep -E.

egrep 'foo|bar' *.txt

(Les parenthèses sont inutiles, sauf si l'alternance fait partie d'une regex plus grande.)


4
En fait, grep -Eest plus standard que egrep.
jw013

8

Si vous n'avez pas besoin d'expressions régulières, il est beaucoup plus rapide d'utiliser fgrepou grep -Favec plusieurs paramètres -e, comme ceci:

fgrep -efoo -ebar *.txt

fgrep(alternativement grep -F) est beaucoup plus rapide que grep normal car il recherche des chaînes fixes au lieu d'expressions régulières.


4
S'il vous plaît voir également les commentaires sur cette page mentionnant que fgrepest déconseillée.
phk

6

Vous pouvez essayer la commande ci-dessous pour obtenir le résultat:

egrep 'rose.*lotus|lotus.*rose' some_file

3

Un moyen peu coûteux et joyeux de rechercher plusieurs motifs:

$ echo "foo" > ewq ; echo "bar" >> ewq ; grep -H -f ewq *.txt ; rm ewq

Cela pourrait bénéficier d'une explication.
Peter Mortensen

2
L'explication est que l' -foption de grep prend un fichier avec plusieurs modèles. Au lieu de créer un fichier temporaire (que vous pourriez oublier de supprimer par la suite), utilisez simplement le processus de substitution du shell:grep -f <(echo foo; echo bar) *.txt
Jakob

3

Pipe ( |) est un caractère spécial dans le shell, il doit donc être échappé ( \|) ou cité selon manual ( man bash):

La citation est utilisée pour supprimer la signification particulière de certains caractères ou mots du shell. Il peut être utilisé pour désactiver le traitement spécial pour les caractères spéciaux, empêcher que les mots réservés ne soient reconnus comme tels et empêcher le développement des paramètres.

Le fait de placer des caractères entre guillemets doubles préserve la valeur littérale de tous les caractères compris entre guillemets

Une barre oblique inversée ( \) est le caractère d'échappement.

Voir: Quels personnages doivent être échappés dans Bash?

Voici quelques exemples (utilisant des outils non encore mentionnés):

  • Utilisant ripgrep:

    • rg "foo|bar" *.txt
    • rg -e foo -e bar *.txt
  • Utilisant git grep:

    • git grep --no-index -e foo --or -e bar

      Remarque: Il prend également en charge les expressions booléennes telles que --and, --oret --not.

Pour un fonctionnement AND par ligne, voir: Comment exécuter grep avec plusieurs modèles AND?

Pour une opération AND par fichier, voir: Comment vérifier si plusieurs chaînes ou expressions rationnelles existent dans un fichier?


3

J'avais des journaux d'accès où les dates étaient bêtement formatées: [30 / Jun / 2013: 08: 00: 45 +0200]

Mais je devais l'afficher comme: 30 / Jun / 2013 08:00:45

Le problème est que, en utilisant "OU" dans mon instruction grep, je recevais les deux expressions de correspondance sur deux lignes distinctes.

Voici la solution:

grep -in myURL_of_interest  *access.log  | \
grep -Eo '(\b[[:digit:]]{2}/[[:upper:]][[:lower:]]{2}/[[:digit:]]{4}|[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}\b)'   \
| paste - - -d" " > MyAccess.log

2

TL; DR: si vous voulez faire plus de choses après l’appariement de l’un des modèles, mettez-les comme ci-dessous. \(pattern1\|pattern2\)

exemple: je souhaite rechercher tous les emplacements où une variable contenant le nom "date" est définie en tant que chaîne ou int. (par exemple, "int cronDate =" ou "String textFormattedDateStamp ="):

cat myfile | grep '\(int\|String\) [a-zA-Z_]*date[a-zA-Z_]* =' 

Avec grep -E, vous n'avez pas besoin d'échapper aux parenthèses ou au tuyau, c'est-à-diregrep -E '(int|String) [a-zA-Z_]*date[a-zA-Z_]* ='


1

Ça marche pour moi

root@gateway:/home/sshuser# aws ec2 describe-instances --instance-ids i-2db0459d |grep 'STATE\|TAG'

**STATE**   80      stopped

**STATE**REASON     Client.UserInitiatedShutdown    Client.UserInitiatedShutdown: User initiated shutdown

**TAGS**    Name    Magento-Testing root@gateway:/home/sshuser#

1

Il y a plusieurs façons de le faire.

  1. grep 'foo\|bar' *.txt
  2. egrep 'foo|bar' *.txt
  3. find . -maxdepth 1 -type f -name "*.txt" | xargs grep 'foo\|bar'
  4. find . -maxdepth 1 -type f -name "*.txt" | xargs egrep 'foo|bar'

Les 3ème et 4ème options ne grèveront que dans les fichiers et éviteront que les répertoires aient .txtleurs noms.
Ainsi, selon votre cas d'utilisation, vous pouvez utiliser l'une des options mentionnées ci-dessus.
Merci!!


0

pour ajouter à la réponse de @ geekosaur , si vous avez plusieurs modèles qui contiennent également des onglets et de l'espace, utilisez la commande suivante

grep -E "foo[[:blank:]]|bar[[:blank:]]"

[[:blank:]]est la classe de caractères RE qui représente un espace ou un caractère de tabulation

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.