
Ich arbeite mit einer Protokolldatei mit folgendem Format:
Oct 12 01:28:26 server program: 192.168.1.105 text for 1.105
Oct 12 01:30:00 server program: 192.168.1.104 text for 1.104
Oct 12 01:30:23 server program: 192.168.1.103 text for 1.103
Oct 12 01:32:39 server program: 192.168.1.101 text for 1.101
Oct 12 02:28:26 server program: 192.168.1.105 text for 1.105
Oct 12 02:30:00 server program: 192.168.1.104 text for 1.104
Oct 12 02:30:23 server program: 192.168.1.103 text for 1.103
Oct 12 02:32:39 server program: 192.168.1.101 text for 1.101
Ich muss dies erreichen:
Oct 12 02:28:26 server program: 192.168.1.105 text for 1.105
Oct 12 02:30:00 server program: 192.168.1.104 text for 1.104
Oct 12 02:30:23 server program: 192.168.1.103 text for 1.103
Oct 12 02:32:39 server program: 192.168.1.101 text for 1.101
Wie kann ich die neue Ausgabe in eine Datei senden? Ich habe Folgendes versucht:
awk '!_[$6]++ {a=$6} END{print a}' logfile
Aber es liefert mir nicht die erwarteten Ergebnisse. Wie kann ich awk oder sed verwenden, um mir nur die eindeutigen Zeilen mit dem letzten Zeitpunkt der Zeichenfolgenübereinstimmung oder basierend auf Datum/Uhrzeit anzuzeigen?
Antwort1
Wenn Sie einen zweiten Durchgang durchführen (was so ziemlich notwendig ist), können Sie auch nur Zeilennummern statt vollständiger Datensätze speichern. Das macht die Logik einfacher.
awk 'NR == FNR {if (z[$6]) y[z[$6]]; z[$6] = FNR; next} !(FNR in y)' logfile logfile
Richtigkeitsnachweis:
Am Ende der Verarbeitung jeder Zeile wird jede bisher verarbeitete Zeilennummerentwederein Wert in z
,oderein Index (kein Wert) in y
, aber nie beides.
Die durch die Werte in dargestellten Zeilen z
stellen am Ende jeder Iteration genau und nur die neuesten Datensätze dar, die bisher für jede IP-Adresse angezeigt wurden.
Die Indizes von y
sind also genau die Linien, die wirnichtzu drucken.
Antwort2
Speichern Sie die gesamte Zeile (unter Verwendung $6
als Array-Index) und END
iterieren Sie über die Elemente des Arrays:
awk '{z[$6]=$0};END{for (i in z) print z[i]}' logfile
Das Ergebnis wird jedoch nicht sortiert. Sie könnten beispielsweise Folgendes tun:
awk '{z[$6]=NR" "$0};END{for (i in z) print z[i]}' logfile | sort -k1,1n | cut -f2-
### this space ^ is a literal TAB
Dadurch wird die Zeilennummer sowie der Zeileninhalt gespeichert, um anschließend nach der Zeilennummer sortieren zu können.
Bei anderen Methoden ist ein zweiter Durchgang erforderlich, um die Sortierung nach Datum zu erhalten (da es sich um ein Protokoll handelt), es werden jedoch doppelte Einträge gedruckt, wenn die Eingabe doppelte Zeilen enthält (also ganze Zeilen), z. B. mit grep
:
awk '{z[$6]=$0};END{for (var in z) print z[var]}' logfile | grep -Fxf- logfile
oder nur mit awk
:
awk 'NR==FNR{z[$6]=$0;next}
FNR==1{for (var in z) y[z[var]]}
$0 in y' logfile logfile
Antwort3
Wenn Sie nur Zeilen vom selben Tag haben, können Sie dies folgendermaßen handhaben:
sort -k6 -k3r logfile | uniq -f3 | sort -k3
Wenn Sie Zeilen für mehr als einen Tag haben, können Sie diesen grundlegenden Ansatz trotzdem verwenden, aber Ihre Sortierung muss dann viel ausgefeilter werden. Der obige Befehl kann nur die Datensätze eines Tages verarbeiten, da er den Zeitanteil des Zeitstempels (z. B. 02:28:26
) als Proxy für den gesamten Zeitstempel verwendet.
Antwort4
Die Logik wird einfacher, wenn die Datei zeilenweise umgekehrt wird
$ tac logfile | awk '!seen[$6]++' | tac
Oct 12 02:28:26 server program: 192.168.1.105 text for 1.105
Oct 12 02:30:00 server program: 192.168.1.104 text for 1.104
Oct 12 02:30:23 server program: 192.168.1.103 text for 1.103
Oct 12 02:32:39 server program: 192.168.1.101 text for 1.101