
Ich habe eine Protokolldatei, die eine Liste sich wiederholender Merkmale ist. Beispiel:
## 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
Ich versuche, einen Weg zu finden, nach einem Block/Cluster von Zeilen zu suchen, die meinen Suchbegriffen entsprechen, und diese dann in Bash zu löschen. Wie Sie im obigen Beispiel sehen können, sind die Zeilen manchmal leer, manchmal gefüllt. Die beste „Lösung“, die ich bisher gefunden habe, ist die Verwendung von sed '/12:30/,+5 d'
oder etwas besser sed '/12:30/,/notify-send/d'
. Das Problem bei beiden ist, dass der erste alle Vorkommen des Zeitstempels löscht und somit mehr als nur einen Protokolleintrag löscht; das Problem mit dem anderen Befehl ist, dass, wenn es zwei oder mehr Einträge mit derselben Zeit und demselben App-Namen gibt, alle übereinstimmenden Einträge gelöscht werden.
Ich habe versucht, etwas zum Laufen zu bekommen, was mir jedoch kläglich misslang, nämlich: sed '/12:30\n^.*$\n^.*$\ntest notification\nnotification\nnotify-send/d' /tmp/notification_log
. Beachten Sie, dass die 2. und 3. Zeile alles sein können (die Zeilen „urgency“ bzw. „icon_path“). Deshalb habe ich Folgendes verwendet ^.*$
(um ehrlich zu sein, bin ich mir nicht einmal sicher, ob das der korrekte reguläre Ausdruck ist).
BEARBEITEN: Bei Verwendung des obigen fehlgeschlagenen Befehls würde ich folgende Ausgabe erwarten:
11:00
low
earlier notification
notification
notify-send
10:46
normal
hello
world
dunstify
Dieser Befehl hatte die Eingabe von:
12:30
*anything*
*anything*
test notification
notification
notify-send
Antwort1
Eigentlich ist es nicht so schwer, vorausgesetzt, alle Cluster sind M Zeilen lang, M ist fest, Cluster überlappen sich nicht und wir müssen nicht nach dem Anfang eines Clusters suchen. In unserem Fall ist M 6.
sed
ermöglicht das Abgleichen mit mehreren Zeilen, aber da normalerweise jeweils eine Zeile verarbeitet wird, müssen Sie zusätzliche Zeilen explizit an den Musterbereich anhängen. Dies geschieht mit N
:
sed 'N;N;N;N;N; /12:30\n.*\n.*\ntest notification\nnotification\nnotify-send/d'
Der Rest ist Ihr Code ohne ^
und $
Anker. Die Anker werden oft mit „dem Anfang der Zeile“ bzw. „dem Ende der Zeile“ in Verbindung gebracht; in sed
sind sie aber wirklich „… des Strings“. Wenn sed
eine Zeile nach der anderen verarbeitet wird, gibt es keinen Unterschied. In unserem Fall sollten wir uns unbedingt merken, dass die Anker „… des Strings“ sind. Sie in die Mitte zu setzen, ergibt keinen Sinn. Es ist nicht so, dass sie nie mit etwas übereinstimmen würden. sed
würde sie gar nicht als Anker interpretieren, sondern als wörtliche ^
und $
.
Es besteht keine Notwendigkeit für „… der Zeile“-Anker in der Mitte einer Zeichenfolge. Jede Zeile außer der letzten endet direkt vor einem Zeilenumbruchzeichen; jede Zeile außer der ersten beginnt direkt nach einem Zeilenumbruchzeichen. Daher reicht match aus \n
.
Vielleicht haben Sie versucht, Anker zu verwenden, um sicherzustellen, dass .*
(was gierig ist und Zeilenumbruchzeichen entsprechen kann) nicht mehr als einer Zeile entspricht. Selbst wenn ^
und $
als „… der Zeile“-Anker fungieren würden, .*
wäre es immer noch gierig. Bedenken Sie Folgendes: Der Musterraum in sed
enthält nach der letzten Zeile* nie ein Zeilenumbruchzeichen. In unserem Fall wissen wir, dass der Musterraum höchstens sechs Zeilen enthält; und wir haben es \n
genau fünfmal verwendet. Dies garantiert, dass jedes Fragment des regulären Ausdrucks nur mit bestimmten Zeilen in einem Cluster übereinstimmen kann.
Dennoch können Anker hilfreich sein. Der obige Befehl kann einen Cluster löschen, der mit endet notify-send-whatever
. $
ist der richtige Weg, dies zu verhindern. Es gibt keine andere Zeit als 12:30
die, die mit übereinstimmt 12:30
; aber es ist anders für 2:30
, also kann es im Allgemeinen ^
auch nützlich sein. Der verbesserte Befehl:
sed 'N;N;N;N;N; /^12:30\n.*\n.*\ntest notification\nnotification\nnotify-send$/d'
* Das heißt nicht, dass am Ende des Musterbereichs niemals ein Zeilenumbruchzeichen stehen kann. Ein Zeilenumbruchzeichen am Ende zeigt an, dass direkt nach dem Zeichen eine Zeile folgt. Es ist die letzte Zeile und sie ist leer. Und danach folgt kein Zeilenumbruchzeichen, also gilt „niemals ein Zeilenumbruchzeichen nach der letzten Zeile“.