Как мне найти в файле многострочный узор?

128

Мне нужно было найти все файлы, содержащие определенный строковый шаблон. Первое решение, которое приходит на ум, - использовать find piped с xargs grep :

find . -iname '*.py' | xargs grep -e 'YOUR_PATTERN'

Но если мне нужно найти шаблоны, которые занимают более одной строки, я застрял, потому что vanilla grep не может найти многострочные шаблоны.

Oli
источник
2
Этот старше, так что я бы сказал, что это не дубликат :)
rogerdpack 03
@rogerdpack При пометке вопросов как дубликатов возраст вопроса является второстепенным, после количества и качества ответов, а также качества вопроса.
tripleee

Ответы:

98

Так я открыл для себя pcregrep, что означает Perl-совместимые регулярные выражения GREP .

Например, вам нужно найти файлы, в которых сразу за переменной ' _name ' следует переменная ' _description ':

find . -iname '*.py' | xargs pcregrep -M '_name.*\n.*_description'

Совет: вам необходимо включить в шаблон символ разрыва строки. В зависимости от вашей платформы это может быть '\ n', \ r ',' \ r \ n ', ...

Oli
источник
7
Как упоминается halka ниже, «вы также можете убедить подстановочный знак точки соответствовать символам новой строки, если вы добавляете (?) В свое регулярное выражение». Затем используйте grep с регулярным выражением perl, добавив -P. найти . -exec grep -nHP '(? s) SELECT. {1,60} FROM. {1,20} имя_таблицы' '{}' \;
Джим
8
pcregrepдоступно на Mac сbrew install pcre
Джаред Бек
1
Еще лучше: использовать -Hкоторый печатает имя файла перед каждым матчем: pcregrep -HM.
Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
97

Почему бы тебе не пойти на awk :

awk '/Start pattern/,/End pattern/' filename
Amit
источник
2
Это намного проще для понимания и использования, awkкоторое есть в большинстве систем * nix.
Али Карбасси
24
отлично! есть ли способ сделать этот матч не жадным?
marcin
3
Как бы вы распечатали имя файла только при совпадении?
bibstha
2
Вы можете показать номера строк совпадений с помощью awk '/Start pattern/,/End pattern/ {printf NR " "; print}' filename. Вы можете сделать это похорошела, давая номера строк фиксированной ширины: awk '/Start pattern/,/End pattern/ {printf "%-4s ", NR; print}' filename.
Роберт
Кажется, это хорошо работает с одним файлом, но что, если я хочу искать в нескольких файлах?
Jinstrong
84

Вот пример использования GNUgrep :

grep -Pzo '_name.*\n.*_description'

-z/ --null-dataОбрабатывать входные и выходные данные как последовательности строк.

Также здесь

Аяз
источник
1
Я думаю, это учитывает только один символ новой строки.
Cloud
1
Мне не удалось использовать grep для многострочного поиска без использования флагов, -zпоэтому он не разделяет поиск на одну строку и -oпечатает только совпадающую часть.
bbaja42
Я обнаружил, что -o заставляет его ничего не печатать, но -l работает, чтобы получить список файлов (моя команда была grep -rzl pattern *, -rzo не сработала)
Benubird
5
Я рекомендую " grep -Pazo " вместо "-Pzo" для файлов, отличных от ASCII. Это лучше, потому что переключатель -z для файлов, отличных от ASCII, может вызвать поведение grep с «двоичными данными», которое изменяет возвращаемые значения. Switch '' -a | --text '' предотвращает это.
rloth 08
Не работает на Mac с git, установленнымbrew reinstall --with-pcre git
Quanlong
21

grep -Pтакже использует libpcre, но установлен гораздо шире. Чтобы найти полный titleраздел html-документа, даже если он занимает несколько строк, вы можете использовать это:

grep -P '(?s)<title>.*</title>' example.html

Поскольку проект PCRE реализует стандарт Perl, используйте документацию perl для справки:

bukzor
источник
Хм, попробовал это только сейчас и, похоже, не сработало ... gist.github.com/rdp/0286d91624930bd11d0169d6a6337c33
rogerdpack
Я не знал, что у grep есть такая опция. Вероятно, из-за этого: это очень экспериментально, и grep -P может предупреждать о нереализованных функциях. ; это в CentOS 7. В Fedora 29: это экспериментально, и grep -P может предупреждать о нереализованных функциях . Конечно, в BSD grep его вообще нет. Было бы неплохо, если бы это не было таким экспериментальным, но приятно вспомнить об этом - хотя я, скорее всего, не буду его использовать.
Pryftan
17

Вот более полезный пример:

pcregrep -Mi "<title>(.*\n){0,5}</title>" afile.html

Он ищет тег заголовка в html-файле, даже если он занимает до 5 строк.

Вот пример неограниченного количества строк:

pcregrep -Mi "(?s)<title>.*</title>" example.html 
Oli
источник
4
Спасибо за это. Я застрял, не понимая, что подстановочный знак не соответствует символу новой строки.
Мэтт
7
@matt: вы также можете убедить подстановочный знак точки соответствовать символам новой строки, если вы добавите (?s)в свое регулярное выражение, например:"(?s)<html>.*</html>"
lubomir.brindza
@matt Конечно, вы можете проверить $(в конце шаблона), чтобы обозначить, что это конец строки - хотя это не то же самое, что помочь вам найти несколько шаблонов линий. См. Также glob(7). Вы также можете найти этот сайт интерес: regular-expressions.info
Pryftan
4

Вы можете использовать альтернативный фильтр grep здесь (отказ от ответственности: я являюсь автором).

Он поддерживает многострочное сопоставление и ограничение поиска определенными типами файлов из коробки:

sift -m --files '* .py' 'ВАШ_АТТЕРН'

(поиск во всех файлах * .py указанного шаблона многострочного регулярного выражения)

Он доступен для всех основных операционных систем. Взгляните на страницу примеров, чтобы увидеть, как ее можно использовать для извлечения многострочных значений из файла XML.

Svent
источник
3

Этот ответ может быть полезен:

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

Для рекурсивного поиска вы можете использовать флаги -R (рекурсивный) и --include (шаблон GLOB). Видеть:

Используйте синтаксис grep --exclude / - include, чтобы не просматривать определенные файлы с помощью grep

albfan
источник
@ Ɖiamond ǤeezeƦ обратите внимание, что редактирование сообщения в LQP ( stackoverflow.com/review/low-quality-posts/19341146 ) делает обзор недействительным, поэтому просто отредактируйте, если уверены, что сообщение необходимо сохранить.
fedorqui 'SO, перестань причинять вред'
2

@Marcin: пример awk нежадный:

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
pbal
источник
Это напечатает весь файл
Герберт
1

Использование ex/ vieditor и опции globstar (синтаксис аналогичен awkи sed):

ex +"/string1/,/string3/p" -R -scq! file.txt

где aaaваша отправная точка и bbbваш конечный текст.

Для рекурсивного поиска попробуйте:

ex +"/aaa/,/bbb/p" -scq! **/*.py

Примечание. Чтобы включить **синтаксис, запустите shopt -s globstar(Bash 4 или zsh).

kenorb
источник