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


128

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

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

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



2
Этот старше, так что я бы сказал, что это не дубликат :)
rogerdpack 03

@rogerdpack При пометке вопросов как дубликатов возраст вопроса является второстепенным, после количества и качества ответов, а также качества вопроса.
tripleee

Ответы:


98

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

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

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

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


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

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 для справки:


Хм, попробовал это только сейчас и, похоже, не сработало ... 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 

4
Спасибо за это. Я застрял, не понимая, что подстановочный знак не соответствует символу новой строки.
Мэтт

7
@matt: вы также можете убедить подстановочный знак точки соответствовать символам новой строки, если вы добавите (?s)в свое регулярное выражение, например:"(?s)<html>.*</html>"
lubomir.brindza

@matt Конечно, вы можете проверить $(в конце шаблона), чтобы обозначить, что это конец строки - хотя это не то же самое, что помочь вам найти несколько шаблонов линий. См. Также glob(7). Вы также можете найти этот сайт интерес: regular-expressions.info
Pryftan


4

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

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

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

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

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


3

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

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

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

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


@ Ɖ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


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).

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.