web-dev-qa-db-fra.com

Regex (grep) pour la recherche multiligne nécessaire

Duplicate possible:
Comment puis-je rechercher un motif multiligne dans un fichier? Utilisez pcregrep

J'utilise un grep pour rechercher un fichier * .sql contenant le mot select suivi du mot customerName suivi du mot from. Cette instruction select peut couvrir plusieurs lignes et peut contenir des onglets et des nouvelles lignes.

J'ai essayé quelques variations sur les points suivants:

$ grep -liIr --include="*.sql" --exclude-dir="\.svn*" --regexp="select[a-zA-Z0-
9+\n\r]*customerName[a-zA-Z0-9+\n\r]*from"

Ceci, cependant, fonctionne juste pour toujours. Quelqu'un peut-il m'aider avec la syntaxe correcte s'il vous plaît?

200
Ciaran Archer

Sans la nécessité d'installer la variante pcprep de grep, vous pouvez effectuer une recherche multiligne avec grep.

$ grep -Pzo "(?s)^(\s*)\N*main.*?{.*?^\1}" *.c

Explication:

-P activer Perl-regexp pour grep (une extension puissante des expressions régulières)

-z supprime la nouvelle ligne à la fin de la ligne, la sous-titrant pour un caractère nul. C'est-à-dire que grep sait où se trouve la fin de la ligne, mais voit l'entrée comme une grosse ligne.

-o imprime uniquement les correspondances. Parce que nous utilisons -z, le fichier entier est comme une seule grande ligne, ainsi s'il y a correspondance, le fichier entier sera imprimé; De cette façon, il ne le fera pas.

En regexp:

(?s) active PCRE_DOTALL, ce qui signifie que . trouve un caractère ou une nouvelle ligne.

\N ne trouve rien sauf la nouvelle ligne, même avec PCRE_DOTALL activé

.*? trouve . en mode non planifié, c'est-à-dire qu'il s'arrête le plus tôt possible.

^ recherche le début de la ligne

\1 backreference au premier groupe (\s*) Ceci est une tentative pour trouver la même indentation de méthode

Comme vous pouvez l'imaginer, cette recherche imprime la méthode principale dans un fichier source C (*.c).

444
albfan

Je ne suis pas très bon en Grèce. Mais votre problème peut être résolu en utilisant la commande AWK . Tu verras

awk '/select/,/from/' *.sql

Le code ci-dessus résultera de la première occurrence de select jusqu'à la première séquence de from. Maintenant, vous devez vérifier si les instructions retournées ont customername ou non. Pour cela, vous pouvez diriger le résultat. Et peut utiliser awk ou grep à nouveau.

153
Amit

Votre problème fondamental est que grep fonctionne une ligne à la fois - il ne peut donc pas trouver une instruction SELECT répartie sur plusieurs lignes.

Votre deuxième problème est que les expressions rationnelles que vous utilisez ne traitent pas de la complexité de ce qui peut apparaître entre SELECT et FROM - en particulier, elle omet les virgules, les points, les espaces, mais aussi les guillemets une chaîne entre guillemets.

Je préférerais utiliser une solution basée sur Perl, Perl lisant des "paragraphes" à la fois et appliquant une expression régulière à cela. L'inconvénient est d'avoir à faire avec la recherche récursive - il existe des modules pour le faire, bien sûr, y compris le module de base File :: Find .

En résumé, pour un seul fichier:

$/ = "\n\n";    # Paragraphs

while (<>)
{
     if ($_ =~ m/SELECT.*customerName.*FROM/mi)
     {
         printf file name
         go to next file
     }
}

Cela doit être encapsulé dans un sous-répertoire qui est ensuite appelé par les méthodes File :: Find.

7
Jonathan Leffler