Wie kann ich bash (grep/sed/etc.) schnappen Sie sich einen Abschnitt von einem logfile zwischen 2 Zeitstempeln?

StackOverflow https://stackoverflow.com/questions/827930

Frage

Ich habe eine Reihe von E-mail-Protokolle:E-mail.log-E-mail.log.0 mail.log.1.gz mail.log.2.gz

jede dieser Dateien enthält chronologisch sortiert, die Zeilen, die beginnen mit dem Zeitstempel, wie:

3. Mai 13:21:12 ...

Wie kann ich leicht greifen jeden log-Eintrag nach einem bestimmten Datum/Zeit und vor einem anderen Datum/Zeit mit bash (und dazugehörige Kommandozeilen-tools), ohne Vergleich jeder einzelnen Zeile?Halten in Geist, dass mein vor-und nach-Termine kann nicht genau entsprechen, alle Einträge in den logfiles.

Es scheint mir, dass ich brauche, um zu bestimmen, den offset der ersten Zeile größer ist als der Start-Zeitstempel und den offset der letzten Zeile weniger als der Letzte Zeitstempel, und schneiden Sie den Abschnitt aus, irgendwie.

War es hilfreich?

Lösung 2

Hier ist eine grundlegende Vorstellung davon, wie es zu tun:

  1. Überprüfen Sie die Datumsstempel auf die Datei , um zu sehen, ob es irrelevent ist
  2. Wenn es könnte relevent wird, entpacken, wenn nötig und untersuchen Sie die erste und letzte Zeile der Datei, um zu sehen, ob es den Start enthält oder Zeit beenden.
  3. Ist dies der Fall, verwenden Sie eine rekursive Funktion , um festzustellen, ob es die Startzeit in der ersten oder zweiten Hälfte der Datei enthält. Mit Hilfe einer rekursiven Funktion ich glaube, Sie mit rund 20 Vergleiche ein beliebiges Datum in einer Million Linie Logfile finden konnte.
  4. Echo die Logdatei (n), um aus dem Versatz des ersten Eintrags auf den Offset des letzten Eintrags (nicht mehr Vergleiche)

Was ich nicht weiß, ist: wie die n-te Zeile einer Datei am besten lesen (wie effizient ist, es zu benutzen tail n + ** n | Kopf 1 **)

Jede Hilfe?

Andere Tipps

Konvertieren Sie Ihre Min- / Max-Daten in "Sekunden seit Epoche",

MIN=`date --date="$1" +%s`
MAX=`date --date="$2" +%s`

Konvertieren Sie die ersten n Wörter in jeder Protokollzeile auf die gleiche,

L_DATE=`echo $LINE | awk '{print $1 $2 ... $n}'`
L_DATE=`date --date="$L_DATE" +%s`

Vergleichen und wegzuwerfen Linien, bis Sie MIN erreichen,

if (( $MIN > $L_DATE )) ; then continue ; fi

Vergleichen und Druckzeilen, bis Sie MAX erreichen,

if (( $L_DATE <= $MAX )) ; then echo $LINE ; fi

Beenden, wenn Sie überschreiten MAX.

if (( $L_DATE > $MAX )) ; then exit 0 ; fi

Das ganze Skript minmaxlog.sh sieht wie folgt aus,

#!/usr/bin/env bash

MIN=`date --date="$1" +%s`
MAX=`date --date="$2" +%s`

while true ; do
    read LINE
    if [ "$LINE" = "" ] ; then break ; fi

    L_DATE=`echo $LINE | awk '{print $1 " " $2 " " $3 " " $4}'`
    L_DATE=`date --date="$L_DATE" +%s`

    if (( $MIN > $L_DATE  )) ; then continue ; fi
    if (( $L_DATE <= $MAX )) ; then echo $LINE ; fi
    if (( $L_DATE >  $MAX )) ; then break ; fi
done

Ich lief es auf diese Datei minmaxlog.input ,

May 5 12:23:45 2009 first line
May 6 12:23:45 2009 second line
May 7 12:23:45 2009 third line
May 9 12:23:45 2009 fourth line
June 1 12:23:45 2009 fifth line
June 3 12:23:45 2009 sixth line

wie diese,

./minmaxlog.sh "May 6" "May 8" < minmaxlog.input

Sie haben bei jeder einzelnen Linie im Bereich schauen Sie (sagen, ob es im Bereich moechten) wollen so vermute ich, Sie nicht jede Zeile in der Datei bedeuten. Als absolutes Minimum, werden Sie bei jeder Zeile in der Datei bis einschließlich die ersten außerhalb des Bereichs suchen (Ich gehe davon aus den Linien sind in Datum / Uhrzeit Reihenfolge).

Dies ist ein ziemlich einfaches Muster:

state = preprint
for every line in file:
    if line.date >= startdate:
        state = print
    if line.date > enddate:
        exit for loop
    if state == print:
        print line

Sie können dies schreiben in awk, Perl, Python, auch COBOL, wenn Sie müssen aber die Logik ist immer das gleiche.

zuerst die Zeilennummern Locating (mit sagen grep) und dann einfach ausdrucken blind, dass dem Zeilenbereich wird nicht helfen, da grep auch bei allen Zeilen suchen hat ( alle von ihnen, nicht nur bis zum ersten außerhalb des Bereichs, und höchstwahrscheinlich zweimal , eine für die erste Zeile und ein für die letzten).

Wenn dies ist etwas, Sie gehen sehr oft tun, können Sie verschieben sich die Mühe zu prüfen, von ‚jedes Mal, wenn Sie es tun‘ auf ‚einmal, wenn die Datei stabilisiert wird‘. Ein Beispiel wäre die Protokolldateizeilen in eine Datenbank, geordnet nach Datum / Uhrzeit zu laden sein.

Das dauert eine Weile eingerichtet bekommen aber in Ihren Abfragen führen viel schneller geworden. Ich bin nicht unbedingt eine Datenbank befürworten - Sie wahrscheinlich den gleichen Effekt durch Aufspalten der Protokolldateien in stündliche Protokolle erreichen könnten also:

2009/
  01/
    01/
      0000.log
      0100.log
      : :
      2300.log
    02/
    : :

Dann für eine bestimmte Zeit, wissen Sie genau, wo Sie anfangen und aufhören suchen. Der Bereich 2009/01/01-15:22 durch 2009/01/05-09:07 würde zu:

  • einige (das letzte Bit) der Datei 2009/01/01/1500.txt.
  • alle Dateien 2009/01/01/1[6-9]*.txt.
  • alle Dateien 2009/01/01/2*.txt.
  • alle Dateien 2009/01/0[2-4]/*.txt.
  • alle Dateien 2009/01/05/0[0-8]*.txt.
  • einige (das erste Bit) der Datei 2009/01/05/0900.txt.

Natürlich würde ich ein Skript schreibe diese Zeilen zurück, anstatt zu versuchen es jedes Mal manuell zu tun.

Vielleicht können Sie versuchen, diese:

sed -n "/BEGIN_DATE/,/END_DATE/p" logfile

Es kann in einer Bash-Umgebung möglich sein, aber Sie sollten wirklich nutzen Werkzeuge nehmen, die für die Arbeit mit Strings und Daten mehr integrierten Unterstützung haben. Zum Beispiel scheint Ruby die in Fähigkeit, Ihr Datumsformat zu analysieren gebaut zu haben. Es kann dann wandelt sie in einen leicht vergleichbaren Unix-Zeitstempel (eine positive ganze Zahl, die die Sekunden seit der Epoche).

irb> require 'time'
# => true

irb> Time.parse("May 3 13:21:12").to_i
# => 1241371272  

Sie können dann schreiben leicht einen Ruby-Skript:

  • Geben Sie ein Start- und Enddatum. Rechnen Sie diejenigen auf diese Unix-Zeitstempel Nummer.
  • Scannen Sie die Log-Dateien Zeile für Zeile, das Datum in seinen Unix-Zeitstempel konvertieren und überprüfen, ob das im Bereich der Start- und Enddaten ist.

Hinweis: Die Umstellung auf eine ganze Zahl Unix Timestamp erste ist nett, weil ganze Zahlen zu vergleichen ist sehr einfach und effizient zu tun

.

Sie haben erwähnt, „ohne jede einzelne Zeile zu vergleichen.“ Sein Gehen schwer zu „erraten“ an, wo in der Log-Datei die Einträge beginnt zu alt oder zu neu, ohne alle Werte dazwischen zu überprüfen. Wenn es jedoch in die Tat ein monoton steigender Trend, dann wissen Sie sofort, wann man aufhören Linien Parsen, denn sobald der nächste Eintrag zu neu ist (oder alt, je nach Layout der Daten) Sie wissen, Sie können aufhören zu suchen. Noch gibt es das Problem, die erste Zeile in Ihrem gewünschten Bereich zu finden.


Ich habe gerade bemerkt, deine Bearbeitung. Hier ist, was ich würde sagen:

Wenn Sie wirklich Sorgen um effizient, dass die Start- und End-Eintrag zu finden, dann könnten Sie für jeden eine binäre Suche. Oder, wenn das übertrieben oder zu schwierig mit bash Tools scheint könnten Sie haben eine Heuristik von nur 5% der Zeilen lesen (1 in jedem 20), um schnell eine nahezu exakte Antwort zu bekommen und dann zu verfeinern, dass, falls gewünscht. Dies sind nur einige Vorschläge für Leistungsverbesserungen.

Ich weiß, dieser thread ist alt, aber ich stolperte über es nach kurzem Suche nach einem one-line-Lösung für meine Bedürfnisse:

awk -v ts_start="2018-11-01" -v ts_end="2018-11-15" -F, '$1>=ts_start && $1<ts_end' myfile

In diesem Fall, meine Datei hat Aufzeichnungen mit durch Komma separierten Werten und dem Zeitstempel des ersten Feldes.Sie verwenden können jede gültig timestamp-format für die start-und endzeitstempel, und ersetzen Sie diese shell-Variablen, falls gewünscht.

Wenn Sie möchten, schreiben Sie eine neue Datei, verwenden Sie einfach normale Ausgabe-Umleitung (> newfile) am Ende oben.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top