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

où 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.