Comment effectuer un calcul sur un fichier journal
Question
J'ai un qui ressemble à ceci:
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]
J'aimerais créer un script shell qui calcule la moyenne des deuxième et troisième champs entre parenthèses ( 840
et 0
dans le dernier exemple). Une question encore plus ardue: est-il possible d’obtenir la moyenne du 3ème champ uniquement lorsque le dernier n'est pas 0
?
Je sais que je pourrais utiliser Ruby
ou un autre langage pour créer un script, mais j'aimerais le faire avec Bash
. Toute bonne suggestion sur les ressources ou astuces sur la manière de créer un tel script serait utile.
La solution
Publier la réponse que je vous ai collée par-dessus la messagerie instantanée ici aussi, juste parce que cela me fait essayer StackOverflow out:)
# replace $2 with the column you want to avg;
awk '{ print $2 }' | perl -ne 'END{ printf "%.2f\n", $total/$n }; chomp; $total+= Publier la réponse que je vous ai collée par-dessus la messagerie instantanée ici aussi, juste parce que cela me fait essayer StackOverflow out:)
<*>; $n++' < log
Autres conseils
Utilisez bash
et awk
:
fichier 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} '
Exemple de sortie (pour vos données d'origine):
avg2 = 2859.59, avg3 = 149.94
Bien sûr, vous n'avez pas besoin d'utiliser cat
, il est inclus ici pour des raisons de lisibilité et pour illustrer le fait que les données d'entrée peuvent provenir de n'importe quel canal. si vous devez utiliser un fichier existant, exécutez sed -ne '...' file | ...
directement.
MODIFIER
Si vous avez accès à gawk
(GNU awk), vous pouvez éliminer le besoin de sed
comme suit:
fichier cat | gawk '{if (match ($ 0, /.*INFO.*\[([0-9,] *) \) [\ r] * $ /, a)) {cnt ++; scission (a [1], b, / *, * /); sum2 + = b [2]; sum3 + = b [3]}} FIN {if (cnt > 0) printf "quot2 =% 2f, avg3 =% 2f \ n ",sum2 / cnt, sum3 / cnt} '
Mêmes remarques concernant. cat
s'applique.
Un peu d'explication:
-
sed
n’imprime que les lignes (combinaison-n ...: p
) correspondant à l’expression régulière (lignes contenant INFO suivies de toute combinaison de chiffres, d'espaces et des virgules entre crochets à la fin de la ligne, permettant les espaces de fin et CR); si une telle ligne correspond, ne conservez que ce qu'il y a entre les crochets (\ 1
, ce qui correspond à ce qu'il y a entre\ (... \)
dans l'expression régulière) avant l'impression (: p
)- sed affichera des lignes ressemblant à:
8541, 931, 0, 0
- sed affichera des lignes ressemblant à:
-
awk
utilise une virgule entourée d'au moins 0 espaces (-F '*, *'
) comme délimiteurs de champs;$ 1
correspond à la première colonne (par exemple 8541),$ 2
à la seconde etc. Les colonnes manquantes comptent comme valeur0
- à la fin,
awk
divise les accumulateurssum2
etc. en nombre d'enregistrements traités,NR
- à la fin,
-
gawk
fait tout d'un coup; il testera d'abord si chaque ligne correspond à la même expression régulière transmise dans l'exemple précédent àsed
(sauf que contrairement àsed
,awk
ne nécessite pas\
entre les parenthèses délimitant des zones ou des intérêts). Si la ligne est la même, ce qui est entre les crochets arrondis se termine par un [1], que nous séparons ensuite à l'aide du même séparateur (une virgule entourée d'un nombre quelconque d'espaces) et que nous utilisons pour l'accumuler. J'ai introduitcnt
au lieu de continuer à utiliserNR
car le nombre d'enregistrements traitésNR
peut être supérieur au nombre réel d'enregistrements pertinents (cnt
) si toutes les lignes ne sont pas de la formeINFO ... [... nombres-séparés par des virgules ...]
, ce qui n'était pas le cas avecsed | awk
puisquesed
garantit que toutes les lignes transmises àawk
sont pertinentes.
Utilisez nawk ou / usr / xpg4 / bin / awk sur Solaris .
awk -F'[],]' 'END {
print s/NR, t/ct
}
{
s += $(NF-3)
if ($(NF-1)) {
t += $(NF-2)
ct++
}
}' infile
Utiliser 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