Come eseguire il calcolo su un file di registro
Domanda
Ne ho uno simile al seguente:
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]
Vorrei creare uno script di shell che calcola la media del 2 ° e 3 ° campo tra parentesi ( 840
e 0
nell'ultimo esempio). Una domanda ancora più difficile: è possibile ottenere la media del 3 ° campo solo quando l'ultimo non è 0
?
So che potrei usare Ruby
o un'altra lingua per creare uno script, ma mi piacerebbe farlo in Bash
. Qualsiasi buon suggerimento su risorse o suggerimenti su come creare un tale script sarebbe di aiuto.
Soluzione
Pubblicando la risposta che ho incollato anche su IM qui, solo perché mi fa provare StackOverflow :)
# replace $2 with the column you want to avg;
awk '{ print $2 }' | perl -ne 'END{ printf "%.2f\n", $total/$n }; chomp; $total+= Pubblicando la risposta che ho incollato anche su IM qui, solo perché mi fa provare StackOverflow :)
<*>; $n++' < log
Altri suggerimenti
Usa bash
e awk
:
file cat | 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} '
Output di esempio (per i tuoi dati originali):
avg2 = 2859.59, avg3 = 149.94
Naturalmente, non è necessario utilizzare cat
, è incluso lì per leggibilità e per illustrare il fatto che i dati di input possono provenire da qualsiasi pipe; se devi operare su un file esistente, esegui il file sed -ne '...' | ...
direttamente.
Modifica
Se hai accesso a gawk
(GNU awk), puoi eliminare la necessità di sed
come segue:
file cat | 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} '
Stesse osservazioni relative a. cat
si applica.
Un po 'di spiegazione:
-
sed
stampa solo le righe (combinazione-n ...: p
) che corrispondono all'espressione regolare (righe contenenti INFO seguite da qualsiasi combinazione di cifre, spazi e virgole tra parentesi quadre alla fine della linea, tenendo conto degli spazi finali e CR); se una di queste righe corrisponde, mantieni solo ciò che è tra parentesi quadre (\ 1
, corrispondente a ciò che è tra\ (... \)
nell'espressione regolare) prima di stampare (: p
)- sed produrrà linee simili a:
8541, 931, 0, 0
- sed produrrà linee simili a:
-
awk
utilizza una virgola circondata da 0 o più spazi (-F '*, *'
) come delimitatori di campo;$ 1
corrisponde alla prima colonna (ad es. 8541),$ 2
alla seconda ecc. Le colonne mancanti contano come valore0
- alla fine,
awk
divide gli accumulatorisum2
ecc. per il numero di record elaborati,NR
- alla fine,
-
gawk
fa tutto in un colpo solo; testerà prima se ogni riga corrisponde alla stessa espressione regolare passata nell'esempio precedente ased
(tranne che a differenza dised
,awk
non richiede un\
tra le parentesi tonde che delimitano aree o interessi). Se la linea corrisponde, ciò che si trova tra le parentesi tonde finisce in un [1], che poi dividiamo usando lo stesso separatore (una virgola circondata da un numero qualsiasi di spazi) e usiamo quello per accumulare. Ho introdottocnt
invece di continuare a usareNR
perché il numero di record elaboratiNR
potrebbe essere maggiore del numero effettivo di record pertinenti (cnt ) se non tutte le righe hanno il formato INFO ... [... virgola-separate-numeri ...]
, che non era il caso dised | awk
poichésed
ha garantito che tutte le righe passate suawk
erano pertinenti.
Usa nawk o / usr / xpg4 / bin / awk su Solaris .
awk -F'[],]' 'END {
print s/NR, t/ct
}
{
s += $(NF-3)
if ($(NF-1)) {
t += $(NF-2)
ct++
}
}' infile
Usa 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