7

Я хочу найти определенную строку в определенном файле. Если я нашел строку, я также хочу напечатать строки X строк до (или после) этой строки.

Можно это с помощью grep или awk, или мне нужна комбинация?

Я хотел бы иметь что - то вроде этого, но не со всеми отставая / ведущие линии до или после удара, только Х.

Например, если мой вход выглядит так:

line1 with a pattern
line2
line3
line4 with a pattern
line5
line6
line7 with a pattern
...

Например, я хочу найти слово «шаблон» и вывести эту строку + строку, которая будет через 2 строки после этого, но не строку, которая следует непосредственно за строкой с шаблоном. Итак, желаемый результат:

line1 with a pattern
line3
line4 with a pattern
line6
line7 with a pattern
...

4 ответа4

14

grep сделает это за вас, с опциями -A (после) и -B (до) и -C (контекст). Пример, который я часто использую:

   sudo lspci -vnn | grep -i net -A 12

потому что эта команда покажет 12 строк после матча, который включает драйвер, используемый для управления сетевой (-i net) картой. В общем, команда это:

  grep text_to_search -A n -B m file.extension

который выведет m строк до совпадения и n строк после совпадения. Или вы можете использовать

 grep text_to_search -C n file.extension

показать всего n строк, окружающих найденный текст (половина до, половина после совпадения).

2

Немного посмотрев на этот вопрос, я думаю, что мне нужно пересмотреть мой комментарий о целесообразности, используя стандартные инструменты GNU без использования сценариев. Это может быть очень трудно сделать из-за особых случаев.

Если вы не возражаете против использования awk, я мог бы предложить следующее решение. Этот скрипт context.awk все еще довольно лаконичен:

{
    lines[NR] = $0
    if (dump[NR]) {
        print $0;
    if ($0 ~ Pattern) {
        if (NR-Delta in lines) {
            print "---"
            print lines[NR-Delta]
        }
        dump[NR+Delta] = 1
        print $0;
    }
    if (NR-Delta in lines) 
        delete lines [NR-Delta];
}

Это должно быть вызвано следующим образом:

awk -v Delta=X -v Pattern=PATTERN -f context.awk sample.txt

где X - желаемое "расстояние контекста", а PATTERN - шаблон поиска. Сценарий пытается разделить несколько контекстов шаблона в файле, печатая строки с --- между ними. Так, например, следующий sample.txt

line1
line2
line3 XXX
line4
line5
line6
line7 XXX
line8
line9
line10 XXX
line11

используя этот вызов

awk -v Delta=3 -v Pattern=XXX -f context.awk sample.txt

даст следующий результат

line3 XXX
line6
---
line4
line7 XXX
line10 XXX
---
line7 XXX
line10 XXX
1

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

sed -n -e '/with a pattern/ {h;n;n;H;x;p}' file

Это кажется немного уродливым, но, добавив дополнительные команды n (пропустить строку, перейти к следующей) и H (сохранить эту строку, добавив ее в буфер удержания), вы можете создать произвольное отношение keep/skip после сопоставления с образцом.

Обратите внимание, что после сопоставления мы первоначально копируем совпадающую строку в буфер хранения с помощью команды h . Наконец, мы меняем местами удержание и шаблон с помощью x и печатаем пространство шаблонов с помощью p . В этот момент sed продолжит обрабатывать файл построчно в поисках другого совпадения.

Удобный справочник по командам sed можно найти здесь.

0

grep -A2 <pattern> file | grep -B1 <pattern> | grep -v "\-\-" у меня работает:

user@box /tmp $ grep -A2 "with a pattern" test.txt | grep -B1 "with a pattern" | grep -v "\-\-"
line1 with a pattern
line3
line4 with a pattern
line6
line7 with a pattern

user@box /tmp $ cat test.txt 
line1 with a pattern
line2
line3
line4 with a pattern
line5
line6
line7 with a pattern

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .