Ich muss einen Text aus dem Protokoll auswählen und speichern, dass das Feld eine Spalte zur neuen Datei enthält.

Im Folgenden finden Sie beispielsweise das Protokollformat

[Mon Dec 07] [error] [client 10.0.0.65] [id "981004"] [file "sample"] [line "84"] [hostname "test"] [uri "/login"] [unique_id "VmVddAo"]
[Mon Dec 07] [error] [client 10.0.0.65] [file "sample"] [line "47"] [id "960015"] [rev "1"] [msg "Request Missing an Accept Header"] [severity "NOTICE"][ver "OWASP_CRS/2.2.9"] [maturity "9"] [accuracy "9"] [tag "MISSING_HEADER_ACCEPT"] [tag "WASCTC/WASC-21"] [tag "OWASP_TOP_10/A7"] [tag "PCI/6.5.10"] [hostname "test"] [uri "/home"] [unique_id "VmVddQo"]

Möchten Sie die Ausgabe wie unten drucken

[Mon Dec 07] [id "981004"] [uri "/login"]
[Mon Dec 07] [id "960015"] [uri "/home"]

Ich habe awk verwendet, um als spaltenweise zu drucken

grep "Mon Dec 07" filename | sed '/\[[a-zA-Z]/\t&/g' | awk -F'\t' '{print $5}'

But i got the below output 

[id "981004"]
[file "sample"]

Because the column are found on different places, for example

[id "981004"] in the 4th column
[id "960015"] in the 6th column 

Wie man den Wert mit like erhält, ist die ID als Schlüssel und innerhalb der doppelten Anführungszeichen der Wert für diesen Schlüssel. Nach Auswahl aller Werte muss es in einer neuen Datei (csv) als Spalte gespeichert werden.

Danke vrs & Mirosław Zalewski

#!/bin/bash

search=$1
log=$2
regexp="s/(\[$search[^]]*\]).+(\[id[^]]*\]).+(\[uri[^]]*\]).+/\1 \2 \3/p"
sed -rn "$regexp" $2

&

perl -n -e '$,=" "; @groups = $_ =~ m/(\[.*?\]).*(\[(?:id|uri).*?\]).*((?-2)).*/ ; print @groups, "\n"' /path/to/log/file.log

Beide arbeiteten ...

-1
Sasee 23 Dez. 2015 im 22:13

2 Antworten

Beste Antwort

Sie können dies mit einem Skript wie dem folgenden tun:

#!/bin/bash

search=$1
log=$2
regexp="s/(\[$search[^]]*\]).+(\[id[^]]*\]).+(\[uri[^]]*\]).+/\1 \2 \3/p"
sed -rn "$regexp" $2

Sie können dieses Programm in einer Datei speichern (z. B. script.sh), es ausführbar machen (chmod +x script.sh) und $ ./script.sh "Mon Dec 07" log.txt ausführen.

Dies ist, was das Skript tut:

  1. Weist dem Skript das erste Argument der Variablen $search (Text, mit dem Sie Zeilen abgleichen möchten) zu, das zweite der Variablen $log (Name einer Protokolldatei).
  2. Erstellt einen regulären Ausdruck für sed
    • (...) bedeutet Gruppierung
    • \[some text\] bedeutet some text in eckigen Klammern (sie werden mit Baclslashes versehen)
    • [^...] bedeutet ein beliebiges Zeichen außer ..., d. H. [^]] bedeutet ein beliebiges Zeichen außer einer schließenden eckigen Klammer (die für die Regex-Beendigung benötigt wird)
    • .+ bedeutet eine beliebige positive Anzahl von Zeichen
    • \1 bedeutet, dass wir Text aus der ersten Gruppe verwenden müssen (siehe erste Aufzählung)
    • Die Optionen von sed -rn bedeuten, dass der Standarddruck jeder Zeile und die Verwendung erweiterter regulärer Ausdrücke unterdrückt werden.
  3. Verwenden Sie den regulären Ausdruck mit sed in Ihrer Protokolldatei log.txt

Ich hoffe, das hilft.

1
vrs 24 Dez. 2015 im 18:44

Dies ist eine schnelle und schmutzige einzeilige Lösung in Perl:

perl -n -e '$,=" "; @groups = $_ =~ m/(\[.*?\]).*(\[(?:id|uri).*?\]).*((?-2)).*/ ; print @groups, "\n"' /path/to/log/file.log

In diesem Fall wird davon ausgegangen, dass das erste Feld das Datum enthält. Es ist keine bestimmte Reihenfolge der Felder id und uri erforderlich, es werden sie jedoch in der Reihenfolge gedruckt, in der sie in der Datei angezeigt werden.


Eine andere, flexiblere, etwas weniger schmutzige und vielzeilige Lösung in Perl:

%seeked = map { $_ => 1 } qw(id uri unique_id severity msg);

while (<>) {
    my $string = $_;
    my $closing = 1;
    while ( $closing != -1 ) {
            $closing = index($string, "]");
            $field = substr($string, 0, $closing+1);
            $field =~ s/^\s+|\s+$//g;
            $string = substr($string, $closing + 1);

            my @content = split(/ /, $field, 4);

            if (scalar @content == 3 and $field !~ m/"/) {
                    print $field . " ";
                    next;
            }

            if ($seeked{ substr($content[0], 1) }) {
                    print $field . " ";
            }
    }
    print "\n";
}

Um es zu verwenden, kopieren Sie diesen Code, fügen Sie ihn in eine Datei ein und speichern Sie ihn als whatever.pl. Geben Sie dann in der Shell Folgendes ein:

perl /path/to/whatever.pl /path/to/log/file.log

In der ersten Codezeile deklarieren Sie in Klammern direkt nach qw Felder, die gedruckt werden sollen. Ändern Sie diese Zeile, wenn Sie andere Felder oder einige dieser Felder nicht möchten.

Diese Lösung kann immer noch keine Felder in der vorgegebenen Reihenfolge drucken und keine Zeichenfolge anstelle des fehlenden optionalen Felds drucken. Dazu benötigen Sie einen Parser. Fügen Sie zuerst Felder in eine Datenstruktur ein und drucken Sie dann am Zeilenende erkannte Felder.

Darüber hinaus lautet der Code zur "Datumserkennung" Very Wrong ™. Es wird geprüft, ob das Feld genau zwei Leerzeichen enthält und kein doppeltes Anführungszeichen enthält. Wenn beide Bedingungen erfüllt sind, wird davon ausgegangen, dass das Feld Datum ist, und es wird gedruckt. Die richtige Lösung würde prüfen, ob die Zeichenfolge ein gültiges Datum darstellt. Dies kann durch Date :: Manip CPAN Modul, muss aber separat installiert werden.


Wenn Sie beliebige Felder in beliebiger Reihenfolge drucken möchten, müssen Sie einen benutzerdefinierten (?) Parser für dieses Dateiformat schreiben. Dies sollte nicht zu schwierig sein, aber es gibt einige Fallstricke, nach denen Sie suchen müssen:

  • Datumsfeld hat impliziten Schlüssel
  • Typfeld hat impliziten Schlüssel
  • Zumindest das Feld "client" gibt keinen Wert an
  • Viele Felder sind optional
  • Einige Felder können mehrmals vorkommen (mindestens "Tag").

Ich denke nicht, dass CSV ein geeignetes Format für diese Art von Daten wäre. Es ist am besten für zweidimensionale Datenstrukturen. Da Ihre Daten optionale Felder und Felder zum Speichern von Listen enthalten, ist etwas Flexibleres - wie XML oder JSON - besser.

Nebenbei bemerkt, es scheint, dass jeder, der diese Protokolle schreibt, dachte, dass es einfach sein wird, sie zu lesen. Ich würde empfehlen, die Dokumentation / den Support des Anbieters zu überprüfen, um festzustellen, ob eine bestimmte Lösung im Sinn ist.

1
Mirek Długosz 24 Dez. 2015 im 22:59