Como executar cálculo durante um arquivo de log
Pergunta
Eu tenho um que se parece com isso:
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]
Eu gostaria de criar um shell script que calcula a média dos 2º e 3º campos entre colchetes (840
e 0
no último exemplo). Uma questão ainda mais difícil: é possível obter a média do 3º campo apenas quando o último não é 0
Eu sei que eu poderia usar Ruby
ou outra linguagem para criar um script, mas eu gostaria de fazê-lo em Bash
. Qualquer boas sugestões sobre recursos ou sugestões em como criar um script como esse ajudaria.
Solução
Postando a resposta I colado a você através de mensagens instantâneas, também aqui, apenas porque me faz tentar StackOverflow out:)
# replace $2 with the column you want to avg;
awk '{ print $2 }' | perl -ne 'END{ printf "%.2f\n", $total/$n }; chomp; $total+= $_; $n++' < log
Outras dicas
Use bash
e 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 }'
Exemplo de saída (para os dados originais):
avg2=2859.59, avg3=149.94
Claro, você não precisa usar cat
, é incluído lá para legibilidade e para ilustrar o fato de que os dados de entrada pode vir de qualquer tubo; se você tiver que operar em um arquivo existente, sed -ne '...' file | ...
executado diretamente.
Editar
Se você tem acesso a gawk
(GNU awk), você pode eliminar a necessidade de sed
da seguinte forma:
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 }'
As mesmas observações re. cat
aplicar.
Um pouco de explicação:
-
sed
imprime apenas as linhas (combinação-n ... :p
) que correspondem à expressão regular (linhas contendo INFO seguido por qualquer combinação de dígitos, espaços e vírgulas entre parêntesis rectos na extremidade da linha, permitindo espaços à direita e CR); Se qualquer um desses jogos de linha, só manter o que está entre colchetes (\1
, correspondente ao que está entre\(...\)
na expressão regular) antes de imprimir (:p
)- sed linhas de saída vontade que se parecem com:
8541, 931, 0, 0
- sed linhas de saída vontade que se parecem com:
-
awk
usa uma vírgula cercado por 0 ou mais espaços (-F ' *, *'
) como delimitadores de campo; corresponde$1
para a primeira coluna (por exemplo, 8541),$2
para o segundo, etc. ausente colunas contam como valor0
- no final,
awk
divide o acumuladoressum2
etc pelo número de registos processados,NR
- no final,
-
gawk
faz tudo de uma só vez; ele irá primeiro teste se cada linha corresponde a mesma expressão regular passada no exemplo anterior parased
(exceto quesed
ao contrário,awk
não requer um\
no fron os parênteses que delimitam áreas ou juros). Se os jogos de linha, o que está entre a rodada suportes acaba em um [1], que, em seguida, dividir usando o mesmo separador (uma vírgula cercado por qualquer número de espaços) e usar isso para acumular. Eu apresenteicnt
vez de continuar a usarNR
porque o número de registrosNR
processado pode ser maior do que o número real de registros relevantes (cnt
) se não todas as linhas são doINFO ... [...comma-separated-numbers...]
forma, o que não foi o caso comsed|awk
desdesed
garantiu que todas as linhas passadas paraawk
foram relevantes.
Use nawk ou / usr / XPG4 / bin / awk em Solaris .
awk -F'[],]' 'END {
print s/NR, t/ct
}
{
s += $(NF-3)
if ($(NF-1)) {
t += $(NF-2)
ct++
}
}' infile
Use 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