
У меня есть файл журнала, который представляет собой список повторяющихся характеристик. Например:
## 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'
* Это не значит, что в конце шаблона никогда не может быть символа новой строки. Символ новой строки в конце указывает на то, что сразу после символа есть строка. Это последняя строка, и она пустая. И после нее нет символа новой строки, поэтому "никогда символ новой строки после последней строки" остается в силе.