Cómo realizar el cálculo sobre un archivo de registro
Pregunta
Tengo una que se parece a esto:
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]
Me gustaría crear un script de shell que calcule el promedio de los campos 2 y 3 entre corchetes ( 840
y 0
en el último ejemplo). Una pregunta aún más difícil: ¿es posible obtener el promedio del tercer campo solo cuando el último no es 0
?
Sé que podría usar Ruby
u otro idioma para crear un script, pero me gustaría hacerlo en Bash
. Cualquier buena sugerencia sobre recursos o sugerencias sobre cómo crear un script de este tipo ayudaría.
Solución
Enviando la respuesta que te pegué a través de IM aquí también, solo porque me hace probar StackOverflow out :)
# replace $2 with the column you want to avg;
awk '{ print $2 }' | perl -ne 'END{ printf "%.2f\n", $total/$n }; chomp; $total+= Enviando la respuesta que te pegué a través de IM aquí también, solo porque me hace probar StackOverflow out :)
<*>; $n++' < log
Otros consejos
Utilice bash
y awk
:
cat file | sed -ne 's: ^. * INFO. * \ [\ ([0-9,] * \) \] [\ r] * $: \ 1: p' | awk -F '*, *' '{sum2 + = $ 2; sum3 + = $ 3} FIN {if (NR > 0) printf " avg2 =%. 2f, avg3 =%. 2f \ n " ;, sum2 / NR, sum3 / NR} '
Salida de muestra (para sus datos originales):
avg2 = 2859.59, avg3 = 149.94
Por supuesto, no es necesario que utilice cat
, se incluye para legibilidad y para ilustrar el hecho de que los datos de entrada pueden provenir de cualquier canalización; si tiene que operar con un archivo existente, ejecute el archivo sed -ne '...' | ...
directamente.
EDIT
Si tiene acceso a gawk
(GNU awk), puede eliminar la necesidad de sed
de la siguiente manera:
cat file | gawk '{if (match ($ 0, /.*INFO.*\\\\\\\\\\\\\\)] [] [\ r] * $ /, a)) {cnt ++; dividir (a [1], b, / *, * /); sum2 + = b [2]; sum3 + = b [3]}} END {if (cnt > 0) printf " avg2 =%. 2f, avg3 =%. 2f \ n " ;, sum2 / cnt, sum3 / cnt} '
Las mismas observaciones re. cat
se aplica.
Un poco de explicación:
-
sed
solo imprime líneas (combinación-n ...: p
) que coinciden con la expresión regular (líneas que contienen INFO seguidas de cualquier combinación de dígitos, espacios y comas entre corchetes al final de la línea, permitiendo los espacios finales y CR); si alguna de estas líneas coincide, solo mantenga lo que está entre los corchetes (\ 1
, correspondiente a lo que hay entre\ (... \)
en la expresión regular) antes de imprimir (: p
)- sed emitirá líneas que parecen:
8541, 931, 0, 0
- sed emitirá líneas que parecen:
-
awk
usa una coma rodeada por 0 o más espacios (-F '*, *'
) como delimitadores de campo;$ 1
corresponde a la primera columna (por ejemplo, 8541),$ 2
a la segunda, etc. Las columnas que faltan cuentan como valor0
- al final,
awk
divide los acumuladoressum2
etc. por el número de registros procesados,NR
- al final,
-
gawk
hace todo de una vez; primero comprobará si cada línea coincide con la misma expresión regular pasada en el ejemplo anterior parased
(excepto que a diferencia desed
,awk
no requiere un\
frente a los paréntesis redondos que delimitan áreas o intereses). Si la línea coincide, lo que está entre los paréntesis redondos termina en un [1], que luego dividimos usando el mismo separador (una coma rodeada por cualquier número de espacios) y lo usamos para acumular. Introdujecnt
en lugar de seguir usandoNR
porque el número de registros procesados ??NR
puede ser mayor que el número real de registros relevantes (cnt ) si no todas las líneas tienen el formato INFO ... [... comas-separados-numbers ...]
, que no fue el caso consed | awk
desdesed
garantizó que todas las líneas pasadas aawk
eran relevantes.
Use nawk oro / usr / xpg4 / bin / awk donde 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