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

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

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

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


person Oli    schedule 30.09.2008    source источник


Ответы (11)


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

awk '/Start pattern/,/End pattern/' filename
person Amit    schedule 15.09.2010
comment
Это намного легче понять, и он использует awk, который поставляется с большинством систем * nix. - person Ali Karbassi; 28.01.2011
comment
Ницца! есть ли способ сделать этот матч не жадным? - person marcin; 04.07.2012
comment
Как бы вы печатали имя файла только при совпадении? - person Bibek Shrestha; 03.09.2012
comment
Вы можете показать номера строк совпадений с помощью awk '/Start pattern/,/End pattern/ {printf NR " "; print}' filename. Вы можете сделать его красивее, задав номерам строк фиксированную ширину: awk '/Start pattern/,/End pattern/ {printf "%-4s ", NR; print}' filename. - person Robert; 06.01.2015
comment
Кажется, это хорошо работает с одним файлом, но что, если я хочу искать в нескольких файлах? - person Jinstrong; 29.06.2018
comment
@marcin, я только что попробовал это с помощью gnu awk 4.2.1, и по умолчанию он кажется жадным только в отношении шаблона начала, поскольку он просто ищет шаблон конца после нахождения шаблона начала. - person Michael Goldshteyn; 28.07.2018
comment
@Jinstrong использует трубы. например, find . -name "*.txt" | xargs -n1 awk '/foo/,/bar/' будет рекурсивно искать все текстовые файлы в текущем каталоге. - person hoefling; 09.09.2018
comment
Используйте grep, чтобы найти список файлов, которые содержат основное слово / слова, которые вы ищете, а затем используйте awk для детализации каждого файла с помощью цикла for ... in - person Paul Allsopp; 27.09.2018
comment
Очевидно, сделать это не жадным - нетривиально unix.stackexchange.com/questions/49601/, однако команда pcregrep может это сделать. - person rogerdpack; 03.12.2018
comment
Спасибо за это! Помогли мне отфильтровать некоторые файлы журналов, которые требовали многострочного соответствия. - person Nuvious; 03.01.2021

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

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

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

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

person Oli    schedule 30.09.2008
comment
Как упоминалось ниже, вы также можете убедить подстановочный знак точки соответствовать символам новой строки, если вы добавляете (?) В свое регулярное выражение. Затем используйте grep с регулярным выражением perl, добавив -P. найти . -exec grep -nHP '(? s) ВЫБРАТЬ. {1,60} ИЗ. {1,20} имя_таблицы' '{}' \; - person Jim; 22.02.2013
comment
pcregrep доступен на Mac с brew install pcre - person Jared Beck; 02.07.2013
comment
Еще лучше: также используйте -H, который печатает имя файла перед каждым совпадением: pcregrep -HM. - person Ciro Santilli 新疆再教育营六四事件ۍ 21.10.2014

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

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

_3 _ / _ 4_ Обрабатывать входные и выходные данные как последовательности строк.

См. Также здесь

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

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

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

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

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

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

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

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

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

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

С помощью серебряного поисковика:

ag 'abc.*(\n|.)*efg'

Здесь может проявиться оптимизация скорости серебряного поисковика.

person Shwaydogg    schedule 13.01.2015

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

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

sift -m --files '*.py' 'YOUR_PATTERN'

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

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

person svent    schedule 22.02.2015

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

awk '{if ($0 ~ /Start pattern/) {triggered=1;}if (triggered) {print; if ($0 ~ /End pattern/) { exit;}}}' filename
person Martin    schedule 23.07.2015

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

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

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

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

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

Использование редактора _1 _ / _ 2_ и опции 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).

person kenorb    schedule 16.10.2015

person    schedule
comment
Это печатает весь файл, хотя - person Herbert; 04.10.2018