Требуется регулярное выражение (grep) для многострочного поиска

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

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

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

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

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

3 ответа

Решение

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

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

Объяснение:

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

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

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

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

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

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

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

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

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

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

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

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

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

Ваша основная проблема в том, что 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
     }
}

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

Другие вопросы по тегам