Wie Berechnung über eine Log-Datei auszuführen
Frage
Ich habe ein, das wie folgt aussieht:
I, [2009-03-04T15:03:25.502546 #17925] INFO -- : [8541, 931, 0, 0]
I, [2009-03-04T15:03:26.094855 #17925] INFO -- : [8545, 6678, 0, 0]
I, [2009-03-04T15:03:26.353079 #17925] INFO -- : [5448, 1598, 185, 0]
I, [2009-03-04T15:03:26.360148 #17925] INFO -- : [8555, 1747, 0, 0]
I, [2009-03-04T15:03:26.367523 #17925] INFO -- : [7630, 278, 0, 0]
I, [2009-03-04T15:03:26.375845 #17925] INFO -- : [7640, 286, 0, 0]
I, [2009-03-04T15:03:26.562425 #17925] INFO -- : [5721, 896, 0, 0]
I, [2009-03-04T15:03:30.951336 #17925] INFO -- : [8551, 4752, 1587, 1]
I, [2009-03-04T15:03:30.960007 #17925] INFO -- : [5709, 5295, 0, 0]
I, [2009-03-04T15:03:30.966612 #17925] INFO -- : [7252, 4928, 0, 0]
I, [2009-03-04T15:03:30.974251 #17925] INFO -- : [8561, 4883, 1, 0]
I, [2009-03-04T15:03:31.230426 #17925] INFO -- : [8563, 3866, 250, 0]
I, [2009-03-04T15:03:31.236830 #17925] INFO -- : [8567, 4122, 0, 0]
I, [2009-03-04T15:03:32.056901 #17925] INFO -- : [5696, 5902, 526, 1]
I, [2009-03-04T15:03:32.086004 #17925] INFO -- : [5805, 793, 0, 0]
I, [2009-03-04T15:03:32.110039 #17925] INFO -- : [5786, 818, 0, 0]
I, [2009-03-04T15:03:32.131433 #17925] INFO -- : [5777, 840, 0, 0]
Ich möchte ein Shell-Skript erstellen, die den Durchschnitt der 2. und 3. Felder in Klammern (840
und 0
im letzten Beispiel) berechnet. Eine noch härtere Frage: ist es möglich, nur den Durchschnitt des dritten Feldes zu erhalten, wenn die letzten nicht 0
Ich weiß, ich Ruby
oder eine andere Sprache verwenden könnte ein Skript zu erstellen, aber ich möchte es in Bash
zu tun. Gute Vorschläge auf Ressourcen oder Hinweise, wie erstellen ein solches Skript würde helfen.
Lösung
Buchung die Antwort, die ich hier zu Ihnen über IM geklebt, nur weil es mich versuchen, macht aus Stackoverflow:)
# replace $2 with the column you want to avg;
awk '{ print $2 }' | perl -ne 'END{ printf "%.2f\n", $total/$n }; chomp; $total+= $_; $n++' < log
Andere Tipps
Mit bash
und awk
:
cat file | sed -ne 's:^.*INFO.*\[\([0-9, ]*\)\][ \r]*$:\1:p' | awk -F ' *, *' '{ sum2 += $2 ; sum3 += $3 } END { if (NR>0) printf "avg2=%.2f, avg3=%.2f\n", sum2/NR, sum3/NR }'
Eine Beispielausgabe (für die Originaldaten):
avg2=2859.59, avg3=149.94
Natürlich können Sie nicht brauchen, um cat
zu verwenden, wird es deswegen dort für die Lesbarkeit und die Tatsache zu verdeutlichen, dass Eingangsdaten von jedem Rohr kommen kann; wenn Sie direkt auf einer Datei, führen sed -ne '...' file | ...
zu betreiben.
Bearbeiten
Wenn Sie Zugriff auf gawk
(GNU awk) haben, können Sie die Notwendigkeit sed
beseitigen wie folgt:
cat file | gawk '{ if(match($0, /.*INFO.*\[([0-9, ]*)\][ \r]*$/, a)) { cnt++; split(a[1], b, / *, */); sum2+=b[2]; sum3+=b[3] } } END { if (cnt>0) printf "avg2=%.2f, avg3=%.2f\n", sum2/cnt, sum3/cnt }'
Gleiche Bemerkungen wieder. cat
gelten.
Ein bisschen Erklärung:
-
sed
druckt nur aus Linien (-n ... :p
Kombination), die den regulären Ausdruck (Zeilen INFO enthalten, gefolgt durch eine beliebige Kombination von Ziffern, Leerstellen und Komma zwischen eckigen Klammern am Ende der Leitung, so dass für nachfolgende Leerzeichen und CR); wenn eine solche Linie übereinstimmt, nur halten, was zwischen den eckigen Klammern ist (\1
, entsprechend, was zwischen\(...\)
im regulären Ausdruck) vor dem Drucken (:p
)- sed Willen Ausgangsleitungen, die wie folgt aussehen:
8541, 931, 0, 0
- sed Willen Ausgangsleitungen, die wie folgt aussehen:
-
awk
verwendet ein Komma umgeben von 0 oder mehr Räumen (-F ' *, *'
) als Feldtrennzeichen;$1
entspricht der ersten Spalte (z.B. 8541),$2
an den zweiten Spalten usw. Fehlende zählen als Wert0
- am Ende,
awk
teilt die Akkumulatorensum2
usw. durch die Anzahl der Datensätze verarbeitet,NR
- am Ende,
-
gawk
tut alles auf einen Schlag; es wird zunächst prüfen, ob jede Zeile im vorherigen Beispiel bestand den gleichen regulären Ausdrucksed
(das außer im Gegensatz zused
,awk
erfordert keine\
in den runden Klammern fron Bereiche oder Interesse begrenzen). Wenn die Linie übereinstimmt, welche zwischen den runden Klammern ist endet in a [1], die wir dann den gleichen Separator aufgespalten unter Verwendung (ein Komma von einem beliebigen Anzahl von Räumen umgeben ist) und verwendet zu akkumulieren. Ich stelltecnt
anstatt weiterhinNR
zu verwenden, da die Anzahl der Datensätze verarbeitetNR
größer sein kann als die tatsächliche Anzahl der relevanten Datensätze (cnt
), wenn nicht alle Zeilen der FormINFO ... [...comma-separated-numbers...]
sind, was nicht der Fall mitsed|awk
war seitsed
garantiert, dass alle aufawk
bestanden Linien waren relevant.
Mit nawk oder / usr / xpg4 / bin / awk auf Solaris .
awk -F'[],]' 'END {
print s/NR, t/ct
}
{
s += $(NF-3)
if ($(NF-1)) {
t += $(NF-2)
ct++
}
}' infile
Mit Python
logfile= open( "somelogfile.log", "r" )
sum2, count2= 0, 0
sum3, count3= 0, 0
for line in logfile:
# find right-most brackets
_, bracket, fieldtext = line.rpartition('[')
datatext, bracket, _ = fieldtext.partition(']')
# split fields and convert to integers
data = map( int, datatext.split(',') )
# compute sums and counts
sum2 += data[1]
count2 += 1
if data[3] != 0:
sum3 += data[2]
count3 += 1
logfile.close()
print sum2, count2, float(sum2)/count2
print sum3, count3, float(sum3)/count3