Как я grep для нескольких шаблонов на нескольких строках?

19

Точнее

Some text
begin
Some text goes here.
end
Some more text

и я хочу извлечь весь блок, который начинается с «начала» до «конца».

с помощью awk мы можем сделать как awk '/begin/,/end/' text.

Как сделать с grep?

Икер
источник
2
Тот же вопрос по Unix и Linux . Не делай этого .
Жиль "ТАК - перестань быть злым"

Ответы:

14

Обновлено 18 ноября 2016 г. (поскольку изменено поведение grep: grep с параметром -P теперь не поддерживает ^и $привязывает [в Ubuntu 16.04 с ядром v: 4.4.0-21-generic]) ( неверное (не) исправление )

$ grep -Pzo "begin(.|\n)*\nend" file
begin
Some text goes here.  
end

примечание: для других команд просто замените якоря '^' & '$' на якорь новой строки '\n' ______________________________

С помощью команды grep:

grep -Pzo "^begin\$(.|\n)*^end$" file

Если вы не хотите включать шаблоны «начало» и «конец» в результат, используйте grep с поддержкой Lookbehind и Lookahead.

grep -Pzo "(?<=^begin$\n)(.|\n)*(?=\n^end$)" file

Также вы можете использовать \Knotify вместо утверждения Lookbehind.

grep -Pzo "^begin$\n\K(.|\n)*(?=\n^end$)" file

\Kопция игнорировать все до сопоставления с шаблоном и игнорировать сам шаблон.
\nиспользуется для предотвращения печати пустых строк из вывода.

Или, как предполагает @AvinashRaj, есть простой простой grep:

grep -Pzo "(?s)^begin$.*?^end$" file

grep -Pzo "^begin\$[\s\S]*?^end$" file

(?s)сообщает grep, что точка должна соответствовать символам новой строки.
[\s\S]соответствует любому символу, который является пробелом или не пробелом.

И их вывод без включения «начало» и «конец» выглядит следующим образом:

grep -Pzo "^begin$\n\K[\s\S]*?(?=\n^end$)" file # or grep -Pzo "(?<=^begin$\n)[\s\S]*?(?=\n^end$)"

grep -Pzo "(?s)(?<=^begin$\n).*?(?=\n^end$)" file

смотрите полный тест всех команд здесь ( устарел, поскольку поведение grep с параметром -P изменено )

Замечания:

^укажите начало линии и $укажите конец линии. они добавляются вокруг «начала» и «конца», чтобы соответствовать им, если они находятся в одной строке.
В двух командах я избежал, $потому что он также использует для «Подстановка команд» ( $(command)), которая позволяет вывод команды заменить имя команды.

От человека grep:

-o, --only-matching
      Print only the matched (non-empty) parts of a matching line,
      with each such part on a separate output line.

-P, --perl-regexp
      Interpret PATTERN as a Perl compatible regular expression (PCRE)

-z, --null-data
      Treat the input as a set of lines, each terminated by a zero byte (the ASCII 
      NUL character) instead of a newline. Like the -Z or --null option, this option 
      can be used with commands like sort -z to process arbitrary file names.
αғsнιη
источник
измените ваш grep, grep -Pzo "(?<=begin\n)(.|\n)*(?=\nend)" fileчтобы не печатать \nсимвол, который существует в начале строки.
Авинаш Радж,
Используйте модификатор DOTALL, чтобы точка также соответствовала символам новой строкиgrep -Pzo "(?s)begin.*?end" file
Avinash Raj
Или просто,grep -Pzo "begin[\s\S]*?end" file
Авинаш Радж,
1
Решение не работает. grep: ein nicht geschütztes ^ oder $ wird mit -Pz nicht unterstütztgrep: a not protected ^ or $ is not supported with -Pz
Выдает
1
Да, я знаю, это в вашем ответе. Я уверен, что это сработало, когда вы опубликовали это, но попробуйте еще раз сегодня. Поведение, grepкажется, изменилось.
Тердон
2

Если вы grepне поддерживаете синтаксис perl ( -P), вы можете попробовать объединить строки, соответствующие шаблону, а затем снова развернуть строки, как показано ниже:

$ tr '\n' , < foo.txt | grep -o "begin.*end" | tr , '\n'
begin
Some text goes here.
end
kenorb
источник