Comment puis-je rechercher plusieurs modèles sur plusieurs lignes?

Réponses:

14

Mis à jour le 18 novembre 2016 (puisque le comportement de grep est modifié: grep avec le paramètre -P ne prend plus en charge ^et $ancre [sur Ubuntu 16.04 avec le noyau v: 4.4.0-21-generic]) (correct (non) correct )

$ grep -Pzo "begin(.|\n)*\nend" file
begin
Some text goes here.  
end

Remarque: pour les autres commandes, remplacez simplement les ancres '^' et '$' par une ancre de nouvelle ligne '\n' ______________________________

Avec la commande grep:

grep -Pzo "^begin\$(.|\n)*^end$" file

Si vous ne voulez pas inclure les modèles "begin" et "end" dans le résultat, utilisez grep avec le support de Lookbehind et Lookahead.

grep -Pzo "(?<=^begin$\n)(.|\n)*(?=\n^end$)" file

Vous pouvez également utiliser \Knotifier au lieu de l'assertion Lookbehind.

grep -Pzo "^begin$\n\K(.|\n)*(?=\n^end$)" file

\Koption ignore tout avant la correspondance de motifs et ignore le motif lui-même.
\nutilisé pour éviter d'imprimer des lignes vides à partir de la sortie.

Ou, comme @AvinashRaj le suggère, il existe un simple grep facile comme suit:

grep -Pzo "(?s)^begin$.*?^end$" file

grep -Pzo "^begin\$[\s\S]*?^end$" file

(?s)indique à grep d'autoriser le point à correspondre aux caractères de nouvelle ligne.
[\s\S]correspond à tout caractère blanc ou non blanc.

Et leur sortie sans inclure "début" et "fin" est la suivante:

grep -Pzo "^begin$\n\K[\s\S]*?(?=\n^end$)" file # or grep -Pzo "(?<=^begin$\n)[\s\S]*?(?=\n^end$)"

grep -Pzo "(?s)(?<=^begin$\n).*?(?=\n^end$)" file

voir le test complet de toutes les commandes ici ( obsolète car le comportement de grep avec le paramètre -P est modifié )

Remarque:

^pointez le début d'une ligne et $pointez la fin d'une ligne. ceux-ci s'ajoutent aux alentours de "début" et "fin" pour les faire correspondre s'ils sont seuls dans une ligne.
Dans deux commandes, je me suis échappé $car il utilise également pour "Substitution de commande" ( $(command)) qui permet à la sortie d'une commande de remplacer le nom de la commande.

De l'homme grep:

-o, --only-matching
      Print only the matched (non-empty) parts of a matching line,
      with each such part on a separate output line.

-P, --perl-regexp
      Interpret PATTERN as a Perl compatible regular expression (PCRE)

-z, --null-data
      Treat the input as a set of lines, each terminated by a zero byte (the ASCII 
      NUL character) instead of a newline. Like the -Z or --null option, this option 
      can be used with commands like sort -z to process arbitrary file names.
αғsнιη
la source
changez votre grep comme grep -Pzo "(?<=begin\n)(.|\n)*(?=\nend)" filepour ne pas imprimer le \ncaractère qui existe sur la ligne commence.
Avinash Raj
Utilisez le modificateur DOTALL pour que le point corresponde également aux caractères de nouvelle lignegrep -Pzo "(?s)begin.*?end" file
Avinash Raj
Ou simplement,grep -Pzo "begin[\s\S]*?end" file
Avinash Raj
1
La siólution ne fonctionne pas. Cela produit une erreur: grep: ein nicht geschütztes ^ oder $ wird mit -Pz nicht unterstütztLa traduction de l'erreur est quelque chose comme:grep: a not protected ^ or $ is not supported with -Pz
musbach
1
Oui, je sais, c'est dans votre réponse. Je suis sûr que cela a fonctionné lorsque vous avez posté cela, mais réessayez aujourd'hui. Le comportement de grepsemble avoir changé.
terdon
2

Dans le cas où votre grepne prend pas en charge la syntaxe perl ( -P), vous pouvez essayer de joindre les lignes, de faire correspondre le modèle, puis de développer à nouveau les lignes comme ci-dessous:

$ tr '\n' , < foo.txt | grep -o "begin.*end" | tr , '\n'
begin
Some text goes here.
end
kenorb
la source