web-dev-qa-db-fra.com

Comment puis-je rechercher plusieurs motifs avec un motif ayant un caractère pipe?

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 recherche en tapant

grep (foo|bar) *.txt

mais le Shell interprète le | en tant que pipe et se plaint lorsque bar n'est pas un exécutable.

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

681
Dan

Tout d'abord, vous devez protéger le modèle contre l'expansion par le shell. La façon la plus simple de le faire est de mettre des guillemets simples autour. Les guillemets simples empêchent l'expansion de quoi que ce soit entre eux (y compris les barres obliques inverses); la seule chose que vous ne pouvez pas faire alors est d'avoir des guillemets simples dans le modèle.

grep -- 'foo*' *.txt

(notez également le -- marqueur de fin d'option pour empêcher certaines implémentations grep, y compris GNU grep, de traiter un fichier appelé -foo-.txt par exemple (qui serait développé par le Shell à partir de *.txt) à prendre comme option (même s'il suit ici un argument de non-option)).

Si vous avez besoin d'un devis unique, vous pouvez l'écrire comme '\'' (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 au moins¹ deux syntaxes pour les modèles. L'ancienne syntaxe par défaut ( expressions régulières de base ) ne prend pas en charge l'alternance (|), même si certaines versions l'ont comme extension, mais écrite avec une barre oblique inverse.

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

La méthode portable consiste à utiliser la nouvelle syntaxe, expressions régulières étendues . Vous devez passer le -E option à grep pour le sélectionner (anciennement cela était fait avec la commande séparée egrep²)

grep -E -- 'foo|bar' *.txt

Une autre possibilité lorsque vous recherchez simplement plusieurs motifs (par opposition à la construction d'un motif complexe en utilisant la disjonction) est de passer plusieurs motifs à grep. Vous pouvez le faire en précédant chaque motif avec le -e option.

grep -e foo -e bar -- *.txt

Ou mettez des motifs sur plusieurs lignes:

grep -- 'foo
bar' *.txt

Ou stockez ces modèles dans un fichier, un par ligne et exécutez

grep -f that-file -- *.txt

Notez que si *.txt se développe en un seul fichier, grep ne préfixe pas les lignes correspondantes avec son nom comme il le fait lorsqu'il y a plusieurs fichiers. Pour contourner ce problème, avec certaines implémentations grep comme GNU grep, vous pouvez utiliser le -H option, ou avec n'importe quelle implémentation, vous pouvez passer /dev/null comme argument supplémentaire.


¹ certaines implémentations grep prennent encore plus en charge celles compatibles avec Perl avec -P, ou ceux augmentés avec -X, -K pour les caractères génériques ksh ...

² tandis que egrep a été déconseillé par POSIX et n'est parfois plus trouvé sur certains systèmes, sur certains autres systèmes comme Solaris lorsque les utilitaires POSIX ou GNU n'ont pas été installés, alors egrep est votre seule option car son /bin/grep ne prend pas en charge -e, -f, -E, \| ou modèles multi-lignes

egrep "foo|bar" *.txt

ou

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

citant sélectivement 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, donc 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 inens inutilement, car j'ai appris des exemples. Maintenant, j'ai appris quelque chose de nouveau. :)

109
user unknown

Comme TC1 l'a dit, -F semble être une option utilisable:

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

$> patterns="foo
bar" 

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

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

egrep 'foo|bar' *.txt

(Les parenthèses ne sont pas nécessaires sauf si l'alternance fait partie d'une expression rationnelle plus grande.)

17
geekosaur

Si vous n'avez pas besoin d'expressions régulières, il est beaucoup plus rapide d'utiliser fgrep ou grep -F avec 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.

8
Moustafa Elqabbany

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

egrep 'rose.*Lotus|lotus.*rose' some_file
6
Abhishek

Le tuyau (|) Est un caractère Shell spécial, il doit donc être échappé (\|) Ou cité conformément au manuel ( man bash ) :

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

La présence de caractères entre guillemets conserve la valeur littérale de tous les caractères dans les guillemets

Une barre oblique inverse entre guillemets (\) Est le caractère d'échappement.

Voir: Quels caractères doivent être échappés dans Bash?

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

  • En utilisant ripgrep :

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

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

      Remarque: il prend également en charge les expressions booléennes telles que --and, --or Et --not.

Pour l'opération AND par ligne, voir: Comment exécuter grep avec plusieurs modèles AND?

Pour l'opération AND par fichier, voir: Comment vérifier que plusieurs chaînes ou expressions régulières existent dans un fichier?

4
kenorb

Un moyen bon marché et joyeux de rechercher plusieurs modèles:

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

J'avais des journaux d'accès où les dates étaient stupidement 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 qu'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
3
tsmets

TL; DR: si vous voulez faire plus de choses après avoir fait correspondre l'un des multiples modèles, mettez-les comme dans \(pattern1\|pattern2\)

exemple: je veux trouver tous les endroits où une variable qui contient le nom 'date' est définie comme une chaîne ou un entier. (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 tube, c'est-à-dire grep -E '(int|String) [a-zA-Z_]*date[a-zA-Z_]* ='

2
jeremysprofile

Ç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
Mansur Ali

Il existe plusieurs façons de procéder.

  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 3e et 4e options ne grepent que dans les fichiers et évitent que les répertoires aient .txt Dans leurs noms.
Ainsi, selon votre cas d'utilisation, vous pouvez utiliser n'importe laquelle des options mentionnées ci-dessus.
Merci!!

1
Bhagyesh Dudhediya

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

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

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

1
Fuseteam