Regex (grep) pour la recherche multi-ligne nécessaire [dupliquer]

possibilité de duplication:

Comment puis-je rechercher un motif multiligne dans un fichier ? Utilisation pcregrep

je suis à court d'un grep pour trouver tout *.fichier sql qui a 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 lignes.

j'ai essayé quelques variantes sur le suivant:

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

cela, cependant, court pour toujours. Quelqu'un peut m'aider avec la syntaxe correcte s'il vous plaît?

168
demandé sur Community 2010-09-15 16:56:24

3 réponses

sans la nécessité d'installer la variante grep pcregrep, vous pouvez faire la recherche multiligne avec grep.

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

explication:

-P activer perl-regexp pour grep (une puissante extension de régulièrement des extensions)

-z supprimer newline à la fin de la ligne, en la remplaçant par le caractère nul. C'est, grep sait où la fin de la ligne, mais voit l'entrée comme une grande ligne.

-o imprimer uniquement de la correspondance. Parce que nous utilisons -z , le fichier entier est comme une seule grande ligne, donc s'il y a une correspondance, le fichier entier serait imprimé; de cette façon il ne fera pas cela.

Dans regexp:

(?s) activer PCRE_DOTALL , ce qui signifie que . trouve n'importe quel caractère ou nouvelle ligne

\N trouver autre chose que newline, même avec PCRE_DOTALL activé

.*? trouver . dans nongreedy mode, qui est, s'arrête dès que possible.

"1519120920 trouver" début de ligne

la référence arrière à premier groupe ( \s* ) C'est d'essayer de trouver de même indentation de la méthode

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

390
répondu albfan 2011-11-15 12:49:44

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

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 déclarations retournées ont customername ou non. Pour cela, vous pouvez rediriger le résultat. Et peut utiliser awk ou grep à nouveau.

143
répondu Amit 2011-11-21 10:32:44

votre problème fondamental est que grep fonctionne une ligne à la fois - de sorte qu'il ne peut pas trouver une déclaration SELECT étalé sur les lignes.

votre deuxième problème est que le regex que vous utilisez ne traite pas de la complexité de ce qui peut apparaître entre SELECT et FROM - en particulier, il omet les virgules, les arrêts complets (périodes) et les blancs, mais aussi les guillemets et tout ce qui peut être à l'intérieur d'une chaîne Citée.

j'irais probablement avec un Perl-basé solution Perl lire "paragraphes" à un moment et en appliquant une regex. L'inconvénient est d'avoir à gérer la recherche récursive - il y a des modules pour le faire, bien sûr, y compris le module de base fichier::Find .

dans les grandes lignes, pour un seul fichier:

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

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

qui doit être enveloppé dans un sub qui est alors invoqué par les méthodes de File::Find.

6
répondu Jonathan Leffler 2010-09-15 13:11:21