Comment rechercher un motif multiligne dans un fichier?


128

J'avais besoin de trouver tous les fichiers contenant un modèle de chaîne spécifique. La première solution qui vient à l'esprit est d'utiliser find piped avec xargs grep :

find . -iname '*.py' | xargs grep -e 'YOUR_PATTERN'

Mais si j'ai besoin de trouver des modèles qui s'étendent sur plus d'une ligne, je suis bloqué parce que vanilla grep ne peut pas trouver de modèles multilignes.



2
Celui-ci est plus âgé, alors je dirais que ce n'est pas un double :)
rogerdpack

@rogerdpack Lorsque vous marquez des questions comme des doublons, l'âge d'une question est une préoccupation tertiaire, après la quantité et la qualité des réponses et la qualité de la question.
tripleee

Réponses:


98

J'ai donc découvert pcregrep qui signifie GREP d'expressions régulières compatibles Perl .

Par exemple, vous devez trouver des fichiers où la variable ' _name ' est immédiatement suivie de la variable ' _description ':

find . -iname '*.py' | xargs pcregrep -M '_name.*\n.*_description'

Conseil: vous devez inclure le caractère de saut de ligne dans votre motif. Selon votre plate-forme, il peut s'agir de '\ n', \ r ',' \ r \ n ', ...


7
Comme mentionné par halka ci-dessous, "vous pouvez également persuader le caractère générique de point de correspondre aux nouvelles lignes si vous ajoutez (?) À votre expression régulière". Ensuite, utilisez grep avec perl regex en ajoutant -P. trouver . -exec grep -nHP '(? s) SELECT. {1,60} FROM. {1,20} nom_table' '{}' \;
Jim

8
pcregrepest disponible sur le mac avecbrew install pcre
Jared Beck

1
Mieux encore: utiliser aussi -Hqui imprime le nom du fichier avant chaque match: pcregrep -HM.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

97

Pourquoi n'allez-vous pas pour awk :

awk '/Start pattern/,/End pattern/' filename

2
C'est beaucoup plus facile à comprendre et à utiliser awkavec la plupart des systèmes * nix.
Ali Karbassi

24
agréable! y a-t-il un moyen de rendre ce match non gourmand?
marcin

3
Comment imprimeriez-vous le nom du fichier uniquement lorsqu'il y a une correspondance?
bibstha

2
Vous pouvez afficher les numéros de ligne des correspondances avec awk '/Start pattern/,/End pattern/ {printf NR " "; print}' filename. Vous pouvez le faire plus joli en donnant les numéros de ligne d' une largeur fixe: awk '/Start pattern/,/End pattern/ {printf "%-4s ", NR; print}' filename.
Robert

Cela semble bien fonctionner sur un seul fichier, mais que faire si je souhaite effectuer une recherche dans plusieurs fichiers?
Jinstrong

84

Voici l'exemple utilisant GNUgrep :

grep -Pzo '_name.*\n.*_description'

-z/ --null-dataTraitez les données d'entrée et de sortie comme des séquences de lignes.

Voir aussi ici


1
Cela ne représente qu'un seul caractère de nouvelle ligne, je pense.
Cloud le

1
Je n'ai pas pu utiliser grep pour la recherche multiligne, sans utiliser de drapeaux -zafin de ne pas diviser la recherche sur une seule ligne et -od'imprimer uniquement la partie correspondante.
bbaja42

J'ai trouvé que -o faisait qu'il n'imprimait rien, mais -l fonctionnait pour obtenir une liste de fichiers (ma commande était grep -rzl pattern *, -rzo ne fonctionnait pas)
Benubird

5
Je recommande « grep -Pazo » au lieu de «-Pzo» pour les fichiers non ASCII. C'est mieux parce que le commutateur -z sur les fichiers non-ASCII peut déclencher le comportement "données binaires" de grep qui change les valeurs de retour. Commutateur '' -a | --text '' empêche cela.
rloth

Ne fonctionne pas sur Mac avec git installé parbrew reinstall --with-pcre git
Quanlong

21

grep -Putilise également libpcre, mais est beaucoup plus largement installé. Pour trouver une titlesection complète d'un document html, même s'il s'étend sur plusieurs lignes, vous pouvez utiliser ceci:

grep -P '(?s)<title>.*</title>' example.html

Puisque le projet PCRE est implémenté selon le standard perl, utilisez la documentation de perl pour référence:


Hmm a essayé cela tout à l'heure et ne semblait pas fonctionner ... gist.github.com/rdp/0286d91624930bd11d0169d6a6337c33
rogerdpack

Je ne savais pas que grep avait cette option. Probablement à cause de ceci: Ceci est hautement expérimental et grep -P peut avertir des fonctionnalités non implémentées. ; c'est sous CentOS 7. Sous Fedora 29: Ceci est expérimental et grep -P peut avertir des fonctionnalités non implémentées . Bien sûr, dans BSD grep, ce n'est pas du tout là. Ce serait bien si ce n'était pas si expérimental, mais c'est bien de se le rappeler - peu mais je suis susceptible de l'utiliser.
Pryftan le

17

Voici un exemple plus utile:

pcregrep -Mi "<title>(.*\n){0,5}</title>" afile.html

Il recherche la balise de titre dans un fichier html même si elle s'étend sur 5 lignes.

Voici un exemple de lignes illimitées:

pcregrep -Mi "(?s)<title>.*</title>" example.html 

4
Merci pour cela. J'étais coincé sans réaliser qu'un caractère générique ne correspondrait pas au caractère de nouvelle ligne.
mat

7
@matt: vous pouvez également persuader le caractère générique de point de correspondre aux nouvelles lignes si vous ajoutez (?s)à votre expression régulière, comme ceci:"(?s)<html>.*</html>"
lubomir.brindza

@matt Bien sûr, vous pouvez vérifier $(à la fin d'un motif) pour signifier que c'est la fin de la ligne - bien que ce ne soit pas la même chose que de vous aider à trouver plusieurs motifs de ligne. Voir aussi glob(7). Vous pourriez également trouver ce site Web intéressant: regular-expressions.info
Pryftan


4

Vous pouvez utiliser l'alternative grep EIPD ici (disclaimer: je suis l'auteur).

Il prend en charge la correspondance multiligne et limite la recherche à des types de fichiers spécifiques prêts à l'emploi:

tamiser -m --files '* .py' 'VOTRE_PATTERN'

(recherchez tous les fichiers * .py pour le modèle d'expression régulière multiligne spécifié)

Il est disponible pour tous les principaux systèmes d'exploitation. Jetez un œil à la page d'exemples pour voir comment elle peut être utilisée pour extraire des valeurs multilignes d'un fichier XML.


3

Cette réponse pourrait être utile:

Regex (grep) pour la recherche multiligne nécessaire

Pour rechercher récursivement, vous pouvez utiliser les indicateurs -R (récursif) et --include (modèle GLOB). Voir:

Utilisez la syntaxe grep --exclude / - include pour ne pas greper certains fichiers


@ Ɖiamond ǤeezeƦ notez que la modification d'un article dans le LQP ( stackoverflow.com/review/low-quality-posts/19341146 ) invalide la révision, alors modifiez simplement si vous êtes sûr que l'article doit être maintenu.
fedorqui 'SO arrêtez de nuire'

2

@Marcin: exemple awk non gourmand:

awk '{if ($0 ~ /Start pattern/) {triggered=1;}if (triggered) {print; if ($0 ~ /End pattern/) { exit;}}}' filename

2
perl -ne 'print if (/begin pattern/../end pattern/)' filename

Cela imprime cependant le fichier entier
Herbert

1

Utilisation de ex/ vieditor et de l' option globstar (syntaxe similaire à awket sed):

ex +"/string1/,/string3/p" -R -scq! file.txt

aaaest votre point de départ et bbbvotre texte de fin.

Pour effectuer une recherche récursive, essayez:

ex +"/aaa/,/bbb/p" -scq! **/*.py

Remarque: pour activer la **syntaxe, exécutez shopt -s globstar(Bash 4 ou zsh).

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.