2012-07-27 4 views
16

Ich habe eine Apache access.log Datei, die um 35 GB in der Größe ist .. greppen durch sie keine Option mehr ist, ohne viel zu warten.Split access.log Datei von Terminen mit Kommandozeilen-Tool

Ich wollte es in viele kleine Dateien unterteilen, indem ich das Datum als Aufteilungskriterium verwende.

Datum ist im Format "[15/Okt/2011: 12: 02: 02 +0000]". Jede Idee, wie könnte ich es nur mit bash scripting, Standard-Textbearbeitungsprogramme (grep, awk, sed, und likes), Verrohrung und Umleitung tun?

Name der Eingabedatei ist access.log. Ich möchte Ausgabedateien Format haben wie access.apache.15_Oct_2011.log (das wäre der Trick, wenn auch nicht schön, wenn das Sortieren ..)

Antwort

16

Eine Möglichkeit, mit awk:

awk 'BEGIN { 
    split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ", months, " ") 
    for (a = 1; a <= 12; a++) 
     m[months[a]] = a 
} 
{ 
    split($4,array,"[:/]"); 
    year = array[3] 
    month = sprintf("%02d", m[array[2]]) 

    print > FILENAME"-"year"_"month".txt" 
}' incendiary.ws-2009 

Dies wird Ausgabedateien wie:

incendiary.ws-2010-2010_04.txt 
incendiary.ws-2010-2010_05.txt 
incendiary.ws-2010-2010_06.txt 
incendiary.ws-2010-2010_07.txt 

Gegen eine 150 MB-Protokolldatei hat die Antwort von chepner 70 Sekunden auf einem 3,4 GHz 8 Core Xeon E31270, während diese Methode dauerte 5 Sekunden.

Ursprüngliche Inspiration: „How to split existing apache logfile by month?

+0

Sie haben Recht, Sir. Ich habe gerade auch Perl-Lösung getestet, und awk Lösung war um 3x schneller. Ich vermute, dass es mit der Tatsache zu tun hat, dass ein Beispiel für awk keine regulären Ausdrücke verwendet, sondern eine einfache String-Aufteilung, die effizienter sein könnte. Als akzeptierte Antwort markieren. –

+0

Ich habe es gerade aktualisiert, um eine bessere Dateinamenausgabe zu haben. –

+0

Oh, und ich benutze definitiv diese auf Produktion gegen 20 GB-Dateien jetzt ohne Probleme. Nimmt ungefähr 2 GB/Minute auf meinem System. –

4

Perl kam zur Rettung:

cat access.log | perl -n -e'[email protected]\[(\d{1,2})/(\w{3})/(\d{4}):@; open(LOG, ">>access.apache.$3_$2_$1.log"); print LOG $_;' 

Nun, es ist nicht genau "Standard" Manipulationsprogramm, aber es ist trotzdem für die Textbearbeitung gemacht.

Ich habe auch verändert Reihenfolge der Argumente in Dateinamen, so dass Dateien wie access.apache.yyyy_mon_dd.log zur leichteren Sortierung genannt werden.

+0

Dies funktionierte für mich, wo die markierte Antwort nicht, und war akzeptabel performant. – cmenning

1

Art hässlich, das ist bash für Sie:

for year in 2010 2011 2012; do 
     for month in jan feb mar apr may jun jul aug sep oct nov dec; do 
      for day in 1 2 3 4 5 6 7 8 9 10 ... 31 ; do 
       cat access.log | grep -i $day/$month/$year > $day-$month-$year.log 
      done 
     done 
    done 
+0

sehr schlau, danke;) das würde für kleine Datei (Dateigröße weniger als Menge von RAM), wie es durch die gesamte Datei etwa 1.116 mal Schleifen funktioniert :) –

+2

sehr wahr, es ist kein effizientes Skript. es wäre gut für den gelegentlichen Gebrauch. Vielen Dank! – ncultra

+1

wäre es schneller, die äußere Schleife abzurollen und die Datei in zwei Durchgängen zu verarbeiten - beim ersten Durchlauf wird die Datei in Einträge nach Jahr aufgeteilt. Der zweite Durchlauf würde dann die Datei jedes Jahres verarbeiten und die Einträge nach Datum aufteilen. Es kann sogar schneller sein, die zweite Schleife aufzurollen und die Datei in drei Durchläufen zu verarbeiten. – ncultra

10

reine bash, einen Durchlauf durch das Zugriffsprotokoll machen:

while read; do 
    [[ $REPLY =~ \[(..)/(...)/(....): ]] 

    d=${BASH_REMATCH[1]} 
    m=${BASH_REMATCH[2]} 
    y=${BASH_REMATCH[3]} 

    #printf -v fname "access.apache.%s_%s_%s.log" ${BASH_REMATCH[@]:1:3} 
    printf -v fname "access.apache.%s_%s_%s.log" $y $m $d 

    echo "$REPLY" >> $fname 
done < access.log 
+3

Die Methode in meiner Antwort ist dramatisch schneller: Gegen eine 150 MB Log-Datei dauerte diese Antwort ** 70 Sekunden ** auf einem 3,4 GHz 8 Core Xeon E31270, während die Methode in mir ** 5 Sekunden ** dauerte. –

+0

Nicht überraschend :) – chepner

+0

Diese Antwort erstellt jedoch Protokolldateien auf einer Tagesbasis nicht auf einer monatlichen Basis. Dies ist weniger, kein Wunder, es ist schneller. –

4

Hier eine awk Version, die lexikalisch sortierbar Protokolldateien ausgibt.

Einige Effizienzverbesserungen: alles in einem Durchgang, nur generieren fname, wenn es nicht das gleiche ist, schließen Sie fname, wenn Sie auf eine neue Datei wechseln (sonst könnten Sie keine Dateideskriptoren mehr haben).

awk -F"[]/:[]" ' 
BEGIN { 
    m2n["Jan"] = 1; m2n["Feb"] = 2; m2n["Mar"] = 3; m2n["Apr"] = 4; 
    m2n["May"] = 5; m2n["Jun"] = 6; m2n["Jul"] = 7; m2n["Aug"] = 8; 
    m2n["Sep"] = 9; m2n["Oct"] = 10; m2n["Nov"] = 11; m2n["Dec"] = 12; 
} 
{ 
    if($4 != pyear || $3 != pmonth || $2 != pday) { 
    pyear = $4 
    pmonth = $3 
    pday = $2 

    if(fname != "") 
     close(fname) 

    fname = sprintf("access_%04d_%02d_%02d.log", $4, m2n[$3], $2) 
    } 
    print > fname 
}' access-log 
0

Ich habe eine leichte Verbesserung zu Theodore Antwort, damit ich Fortschritte sehen konnte, wenn eine sehr große Protokolldatei zu verarbeiten.

#!/usr/bin/awk -f 

BEGIN { 
    split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ", months, " ") 
    for (a = 1; a <= 12; a++) 
     m[months[a]] = a 
} 
{ 
    split($4, array, "[:/]") 
    year = array[3] 
    month = sprintf("%02d", m[array[2]]) 

    current = year "-" month 
    if (last != current) 
     print current 
    last = current 

    print >> FILENAME "-" year "-" month ".txt" 
} 

Auch fand ich, dass ich brauchte gawk verwenden für diese (brew install gawk, wenn Sie es nicht haben) unter Mac OS X zu arbeiten

1

kombiniert ich Theodors und Thor Lösungen Thors Effizienzsteigerung zu nutzen und tägliche Dateien, behalten aber die ursprüngliche Unterstützung für IPv6-Adressen in einer kombinierten Formatdatei bei.

awk ' 
BEGIN { 
    m2n["Jan"] = 1; m2n["Feb"] = 2; m2n["Mar"] = 3; m2n["Apr"] = 4; 
    m2n["May"] = 5; m2n["Jun"] = 6; m2n["Jul"] = 7; m2n["Aug"] = 8; 
    m2n["Sep"] = 9; m2n["Oct"] = 10; m2n["Nov"] = 11; m2n["Dec"] = 12; 
} 
{ 
    split($4, a, "[]/:[]") 
    if(a[4] != pyear || a[3] != pmonth || a[2] != pday) { 
    pyear = a[4] 
    pmonth = a[3] 
    pday = a[2] 

    if(fname != "") 
     close(fname) 

    fname = sprintf("access_%04d-%02d-%02d.log", a[4], m2n[a[3]], a[2]) 
    } 
    print >> fname 
}' 
+0

Das ist wirklich beeindruckend! Vielen Dank –