我有一个日志文件,其中列出了重复的特征。例如:
## 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
。请注意,第二行和第三行可以是任何内容(分别是紧急程度和 icon_path 行),这就是我使用的原因^.*$
(坦率地说,我甚至不确定这是否是正确的正则表达式)。
编辑:使用上述失败的命令,我期望输出是:
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'
* 这并不意味着模式空间末尾永远不能有换行符。末尾的换行符表示该字符后面紧接着一行。它是最后一行,并且是空的。并且它后面没有换行符,因此“最后一行后面永远不能有换行符”是成立的。