grep пропустить n строк файла и искать только после

9

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

Например:

123
XXY
214
ABC
182
558
ABC
856
ABC

В моем примере я хотел бы найти, 182а затем найти следующее вхождениеABC

Первое появление простое:

grep -n -m1 "182" /var/log/file

Это выводит:

5:182

Как мне найти следующее вхождение ABC?

Моя идея заключалась в том, чтобы сказать, grepчтобы пропустить первые nстроки (в приведенном выше примере n=5), на основе номера строки 182. Но как мне это сделать?

koljanep
источник
1
Это требование, grepкоторое используется? Я не думаю, что это можно сделать, grepно было бы легко с awkили sed(самостоятельно или в сочетании с grep).
Хауке Лагинг
@HaukeLaging grepне требуется. Я еще не очень знаком с sedили awk. Если у вас есть хорошее решение, дайте мне услышать это! :) @don_crissti должна быть напечатана только первая строка. Я не забочусь о других случаях.
koljanep

Ответы:

10

С помощью sedвы можете использовать диапазон и qвводить ввод при одном завершении:

sed '/^182$/p;//,/^ABC$/!d;/^ABC$/!d;q'

Аналогично с GNU grepвы можете разделить ввод между двумя greps:

{ grep -nxF -m1 182; grep -nxF -m1 ABC; } <<\IN
123
XXY
214
ABC
182
558
ABC
856
ABC
IN

... который печатает ...

5:182
2:ABC

... чтобы показать, что первый grepобнаружил, что -Fлитерал с ixed-строкой, -xвся строка 182 соответствует 5 строкам от начала его чтения, а вторая нашла аналогично набранный ABC, совпадающий с 2 ​​строками с начала его чтения - или 2 строками после первого grep прекращения чтения в строке 5.

От man grep:

-m NUM, --max-count=NUM
          Stop  reading  a  file  after  NUM  matching
          lines.   If the input is standard input from
          a regular file, and NUM matching  lines  are
          output, grep ensures that the standard input
          is  positioned  to  just  after   the   last
          matching  line before exiting, regardless of
          the  presence  of  trailing  context  lines.
          This  enables  a calling process to resume a
          search. 

Я использовал здесь документ для воспроизводимой демонстрации, но вы, вероятно, должны сделать:

{ grep ...; grep ...; } </path/to/log.file

Он также будет работать с другими конструкциями составных команд оболочки, такими как:

for p in 182 ABC; do grep -nxFm1 "$p"; done </path/to/log.file
mikeserv
источник
+1 Увидел это в справочной странице. Это то, что я пробовал, только с трубкой между ними grepвместо ;... no-go
Xen2050
@ Xen2050 - канал обычно не работает - обычно, когда вы делитесь вводом, вам нужен файл, который можно найти.
mikeserv
Впечатляющий ответ, но я не поддерживаю ваше утверждение о трубопроводах. Здесь документ, который grepразделяют эти два, является фактически конвейером для них. Что-то еще: я пытался без печати линии маркера, но sed '//,/^ABC$/!d;/^ABC$/!d;q'выдает странную ошибку. Что делает //?
Хауке Лагинг
1
@HaukeLaging - здесь-документ (в большинстве оболочек) не является конвейером - это настоящий tmp-файл, созданный оболочкой, который оболочка удаляет перед выполнением любых записей - при сохранении дескриптора. Это все еще можно найти. Трубы, как правило, не доступны. Я посмотрю на sedвещь - просто написал это очень быстро.
mikeserv
1
@HaukeLaging - О, так что sedвсе работает - вы просто не указали. В sedпоследнем вы можете обратиться к последнему /address/с пустым //адресом. Так /^182$/command;//,/next_address/просто /^182$/command;/^182$/,/next_address/. Вероятно, вашей ошибкой не было предыдущего регулярного выражения, если вы использовали GNU sed. Между прочим, с помощью функции pipe lseek можно манипулировать через косвенные /dev/fd/[num]ссылки через ссылки в системах Linux - но если вы не очень осторожны в том, чтобы правильно обрабатывать буферы (например, с помощью dd) , это обычно проигрышная битва.
mikeserv
2

Используйте grepс Perl-совместимыми регулярными выражениями ( pcregrep):

pcregrep -Mo '182(.|\n)*?\KABC'

Опция -Mпозволяет шаблону соответствовать более чем одной строке и \Kне включает сопоставленный шаблон (до этой точки) в вывод. Вы можете удалить, \Kесли вы хотите получить весь регион в результате.

jimmij
источник
2
> awk '/^182$/ { startline=1; }; startline == 0 { next; }; /^ABC$/ { print "line " NR ": " $0; exit; }' file
line 7: ABC
Хауке Лагинг
источник
1
Это дает первую азбуку где угодно ; этот вопрос требует первого ABC после первых 182. Наиболее прямым является флаг, подобный awk '/^182$/{z=1;next} z&&/^ABC$/{print NR":"$0;exit}' file- или вы можете написать хотя бы один явный getline()цикл, который обычно неуклюже, или быть умным (?), используя диапазон почти такой же, как в perl @ JRFerguson:awk '!x&&/^182$/,/^ABC$/ {x=NR":"$0} END{print x}
dave_thompson_085
@ dave_thompson_085 Действительно. Правильная идея, но ужасно закодированная (перепутала две идеи во время письма). Смущенно я даже пытался, но не удивился на выходе.
Хауке Лагинг
1

Вариант Perl, который вы можете использовать:

perl -nle 'm/182/../ABC/ and print' file

... который печатает строки в соответствующем диапазоне.

Если файл содержит более одного совпадающего диапазона, вы можете ограничить вывод только первым диапазоном, изменив /разделитель на?

perl -nle 'm?182?..?ABC? and print'
JRFerguson
источник
1

Придерживаясь только grepи добавив tail& cut, вы могли бы ...

grep для номера строки первого совпадения 182:

grep -m 1 -n 182 /var/log/file |cut -f1 -d:

Используйте это для grep для всех ABC's только после первой соответствующей строки выше, используя tail' s -n +Kдля вывода после K '-ой строки. Все вместе:

tail -n +$(grep -m 1 -n 182 /var/log/file |cut -f1 -d:) /var/log/file | grep ABC

Или добавьте еще -m 1раз, чтобы найти только первое соответствиеABC

tail -n +$(grep -m 1 -n 182 /var/log/file|cut -f1 -d:) /var/log/file|grep -m 1 ABC

Ссылки:
manстраницы
/programming/6958841/use-grep-to-report-back-only-line-numbers

Xen2050
источник
1

Другой вариант такой:

grep -n -A99999 "182" /var/log/file|grep -n -m1 "ABC"

Флаг -A после матча набирает n строк, и 99999 просто для того, чтобы мы ничего не пропустили. Большие файлы должны иметь больше строк (проверьте с помощью "wc -l").

Fabbe
источник
0

Оператор диапазона ,можно использовать здесь:

< yourfile \
sed -e '
   /182/,/ABC/!d
   //!d;=;/ABC/q
' | sed -e 'N;s/\n/:/'

Оператор диапазона ..в тандеме с оператором «только один раз совпадения» m??можно использовать здесь вPerl

perl -lne 'm?182? .. m?ABC? and print "$.:$_" if /182/ || /ABC/' yourfile

источник