Regex (grep) для многострочного поиска

Возможный дубликат:
Как искать многострочный шаблон в файле? Использовать pcregrep

Я запустил grep, чтобы найти файл *.sql, который имеет слово select, за которым следует слово customerName, за которым следует слово from. Этот оператор select может охватывать множество строк и может содержать табуляции и новые строки.

Я пробовал несколько вариантов:

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

Это, однако, просто работает вечно. Может ли кто-нибудь помочь мне с правильным синтаксисом, пожалуйста?

Ответ 1

Без необходимости устанавливать вариант grep pcregrep, вы можете выполнять многострочный поиск с помощью grep.

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

Объяснение:

-P активировать perl-regexp для grep (мощное расширение регулярных выражений)

-z подавляет -z новой строки в конце строки, заменяя его нулевым символом. То есть grep знает, где находится конец строки, но видит входные данные как одну большую строку.

-o только для печати. Поскольку мы используем -z, весь файл похож на одну большую строку, поэтому при совпадении будет напечатан весь файл; таким образом, это не будет делать это.

В регулярном выражении:

(?s) активировать PCRE_DOTALL, а это значит . находит любой символ или перевод строки

\N найти что-нибудь кроме новой строки, даже с активированным PCRE_DOTALL

.*? найти . в несжатом режиме, то есть останавливается как можно скорее.

^ найти начало строки

\1 обратная ссылка на первую группу (\s*) Это попытка найти тот же отступ метода

Как вы можете себе представить, этот поиск печатает метод main в исходном файле C (*.c).

Ответ 2

Я не очень хорош в grep. Но ваша проблема может быть решена с помощью команды AWK. Просто посмотрите

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

Вышеприведенный код будет результатом первого появления select до первой последовательности from. Теперь вам нужно проверить, имеют ли возвращенные операторы customername или нет. Для этого вы можете передать результат. И может снова использовать awk или grep.

Ответ 3

Ваша основная проблема заключается в том, что grep работает по одной строке за раз - поэтому он не может найти инструкцию SELECT по строкам.

Вторая проблема заключается в том, что используемое вами регулярное выражение не связано со сложностью того, что может появиться между SELECT и FROM - в частности, оно не содержит запятых, полных остановок (периодов) и пробелов, но также кавычек и всего, что может находиться внутри строки с кавычками.

Я, скорее всего, поеду с решением на основе Perl, имея Perl для чтения "абзацев" за раз и применяя к нему регулярное выражение. Недостаток имеет дело с рекурсивным поиском - для этого есть модули, которые, конечно, включают в себя основной модуль File:: Find.

В контуре для одного файла:

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

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

Это должно быть завернуто в sub, который затем вызывается методами File:: Find.