Как выполнить поиск текста в файле и отобразить абзац с текстом?


24

Ниже приведен текст в файле:

Pseudo name=Apple
Code=42B
state=fault

Pseudo name=Prance
Code=43B
state=good

Мне нужно grep для "42B" и получить вывод из приведенного выше текста, как:

Pseudo name=Apple
Code=42B
state=fault

Кто - нибудь есть идеи о том , как добиться этого с помощью grep/ awk/ sed?


Вы пометили этот вопрос просто "grep". Вы ищете только "grep" решения? В вопросе вы также указываете awk & sed. Можем ли мы добавить эти теги? Я не был уверен в твоих намерениях, когда редактировал вопрос прошлой ночью.
SLM

Ответы:


38

С awk

awk -v RS='' '/42B/' file

RS=изменяет разделитель входной записи с новой строки на пустые строки. Если какое-либо поле в записи содержит /42B/распечатку записи.

''(пустая строка) - это магическое значение, используемое для представления пустых строк в соответствии с POSIX :

Если RS равно нулю, то записи разделяются последовательностями, состоящими из <newline>плюс одной или нескольких пустых строк, начальные или конечные пустые строки не должны приводить к пустым записям в начале или конце ввода, и a <newline>всегда должен быть разделителем полей, независимо от того, какова ценность FS .

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

awk -v RS='' -v ORS='\n\n' '/42B/' file

1
+1 за элегантное решение. Вам не нужно перенаправлять файл, хотя ...
jasonwryan

пальцы были на автопилоте.
Луа

2
@jasonwryan, если вам не нужен доступ к имени файла в awk ( FILENAME), неплохо использовать перенаправление, так как это позволяет избежать проблем с именами файлов, содержащими =или начинающимися с -(или существующими -), создает согласованные сообщения об ошибках и избегает выполнения awkили выполнения другие перенаправления, если входной файл не может быть открыт.
Стефан Шазелас

14

Предполагая, что данные структурированы таким образом, что вы всегда хотите использовать строку до и после этого, вы можете использовать переключатели grep -A(after) и -B(before), чтобы указывать, что они включают 1 строку перед совпадением и 1 строку после него:

$ grep -A 1 -B 1 "42B" sample.txt
Pseudo name=Apple
Code=42B
state=fault

Если вам нужны одинаковые номера строк до и после поискового запроса, вы можете использовать -Cпереключатель (context):

$ grep -C 1 "42B" sample.txt
Pseudo name=Apple
Code=42B
state=fault

Если вы хотите быть более строгим при сопоставлении нескольких строк, вы можете использовать инструмент pcregrep, чтобы сопоставить шаблон с несколькими строками:

$ pcregrep -M 'Pseudo.*\n.*42B.*\nstate.*' sample.txt
Pseudo name=Apple
Code=42B
state=fault

Приведенный выше шаблон соответствует следующему:

  • -M - несколько строк
  • 'Pseudo.*\n.*42B.*\nstate.*'- соответствует группе строк, где первая строка начинается со слова, "Pseudo"за которым следуют любые символы до конца строки \n, за которыми следуют любые символы до строки, "42B"за которыми следуют любые символы до другого конца строки ( \n), за которым следует строка "state"следуют любые символы.

5
-C(контекст) можно использовать как ярлык, если -Aи -Bсовпадают.
Дэвид Баггерман

@DavidBaggerman - спасибо. Добавил это в ответ.
SLM

Почему один голос? Это отвечает на вопрос.
SLM

4

Вероятно, есть такой же простой способ сделать это с помощью awk, но в perl:

cat file | perl -ne 'BEGIN { $/="\n\n" }; print if $_ =~ /42B/;'

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


10
Это может быть упрощено путем использования опций и сокращений, а также потери бесполезного использованияcat ; perl -00 -ne 'print if /42B/' file
tripleee

4

grepНекоторые разновидности Unix имеет -pфлаг для «пункта». Я знаю, что AIX делает .

grep -p 42B <myfile>

будет делать именно то, что вы просите там. У grep у YMMV и GNU нет этого флага.


Наличие флага -p было бы замечательно. Особенно если использовать вместе с -v, чтобы вы могли исключить из вывода целые абзацы.
IllvilJa

2

Другое решение Perl, без завершающей пустой строки:

perl -00ne 'if ($_ =~ /42B/) {chomp($_); printf "%s\n",$_}' foo

пример

% perl -00ne 'if ($_ =~ /42B/) {chomp($_); printf "%s\n",$_}' foo
Pseudo name=Apple
Code=42B
state=fault

% cat foo
Pseudo name=Apple
Code=42B
state=fault

Pseudo name=Prance
Code=43B
state=good

1
Или короче (и , следовательно , более удобным для чтения), а triplee написал в комментарии: perl -00 -ne 'print if /42B/' file.
MIVK
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.