Как выполнить поиск в файле по группе строк, а затем удалить эти строки?

Как выполнить поиск в файле по группе строк, а затем удалить эти строки?

У меня есть файл журнала, который представляет собой список повторяющихся характеристик. Например:

## This is the pattern of lines
time
urgency
icon_path
summary
body
appname

## Below is what the log file would actually look like
12:30
critical

test notification
notification
notify-send
11:00
low

earlier notification
notification
notify-send
10:46
normal

hello
world
dunstify

Я пытаюсь найти способ поиска блока/кластера строк, соответствующих моим поисковым запросам, а затем удалить их в bash. Как вы можете видеть в приведенном выше примере, иногда строки пустые, иногда они заполнены. Лучшее «решение», которое я нашел до сих пор, — это использовать sed '/12:30/,+5 d'или немного лучше sed '/12:30/,/notify-send/d'. Проблема с обеими этими командами в том, что первая команда удалит все вхождения временной метки, таким образом удаляя больше, чем одну запись журнала; проблема с другой командой в том, что если есть две или более записей с одинаковым временем и именем приложения, все соответствующие записи будут удалены.

То, что я пытался заставить работать и потерпел сокрушительную неудачу, — это сделать что-то вроде: sed '/12:30\n^.*$\n^.*$\ntest notification\nnotification\nnotify-send/d' /tmp/notification_logОбратите внимание, что 2-я и 3-я строки могут быть чем угодно (строки urgency и icon_path соответственно), поэтому я использовал ^.*$(честно говоря, я даже не уверен, является ли это правильным регулярным выражением).

EDIT: Используя приведенную выше невыполненную команду, я ожидаю, что вывод будет следующим:

11:00
low

earlier notification
notification
notify-send
10:46
normal

hello
world
dunstify

Эта команда имела следующие входные данные:

12:30
*anything*
*anything*
test notification
notification
notify-send

решение1

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

sedпозволяет вам сопоставлять несколько строк, но поскольку он обычно обрабатывает одну строку за раз, вам необходимо явно добавлять дополнительные строки в пространство шаблона. Вы делаете это с помощью N:

sed 'N;N;N;N;N; /12:30\n.*\n.*\ntest notification\nnotification\nnotify-send/d'

Остальное — ваш код без якорей ^и $. Якоря часто ассоциируются с «началом строки» и «концом строки» соответственно; но на sedсамом деле они являются «… строки». При sedобработке одной строки за раз разницы нет. В нашем случае мы определенно должны помнить, что якоря — это «… строки». Размещение их в середине не имеет смысла. Дело не в том, что они никогда ничему не будут соответствовать. sedне будет интерпретировать их как якоря изначально, он будет интерпретировать их как литеральные ^и $.

Нет необходимости в якорях "… of the line" в середине строки. Любая строка, кроме последней, заканчивается прямо перед символом новой строки; любая строка, кроме первой, начинается прямо после символа новой строки. Поэтому достаточно сопоставить \n.

Возможно, вы пытались использовать якоря, чтобы убедиться, что .*(что является жадным и может соответствовать символам новой строки) не соответствует более чем одной строке. Даже если ^и $действуют как якоря "… строки", .*все равно будут жадными. Подумайте об этом: пространство шаблона в sedникогда не содержит символ новой строки после последней строки*. В нашем случае мы знаем, что в пространстве шаблона не более шести строк; и мы использовали \nровно пять раз. Это гарантирует, что каждый фрагмент регулярного выражения может соответствовать только определенной строке в кластере.

Тем не менее, якоря могут помочь. Приведенная выше команда может удалить кластер, заканчивающийся на notify-send-whatever. $— правильный способ предотвратить это. Нет другого времени, кроме 12:30того, которое соответствует 12:30; но оно отличается для 2:30, поэтому в целом ^также может быть полезно. Улучшенная команда:

sed 'N;N;N;N;N; /^12:30\n.*\n.*\ntest notification\nnotification\nnotify-send$/d'

* Это не значит, что в конце шаблона никогда не может быть символа новой строки. Символ новой строки в конце указывает на то, что сразу после символа есть строка. Это последняя строка, и она пустая. И после нее нет символа новой строки, поэтому "никогда символ новой строки после последней строки" остается в силе.

Связанный контент