题
我有一个看起来像这样的:
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]
我想创建一个 shell 脚本来计算括号中第二个和第三个字段的平均值(840
和 0
在最后一个例子中)。一个更棘手的问题:是否只有在最后一场没有的情况下才能得到第三场的平均值 0
?
我知道我可以使用 Ruby
或其他语言来创建脚本,但我想用 Bash
. 。有关资源的任何好的建议或如何创建此类脚本的提示都会有所帮助。
解决方案
在这里发布我通过IM粘贴给你的回复,只是因为它让我尝试StackOverflow:)
# replace $2 with the column you want to avg;
awk '{ print $2 }' | perl -ne 'END{ printf "%.2f\n", $total/$n }; chomp; $total+= 在这里发布我通过IM粘贴给你的回复,只是因为它让我尝试StackOverflow:)
<*>; $n++' < log
其他提示
使用 bash
和 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 }'
示例输出(针对您的原始数据):
avg2=2859.59, avg3=149.94
当然,你不需要使用 cat
, ,它被包含在那里是为了易读并说明输入数据可以来自任何管道的事实;如果您必须对现有文件进行操作,请运行 sed -ne '...' file | ...
直接地。
编辑
如果您有权访问 gawk
(GNU awk),您可以消除对 sed
如下:
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 }'
同样的评论。 cat
申请。
一点解释:
sed
只打印出行(-n ... :p
组合)与正则表达式匹配(包含 INFO 的行,后跟行尾方括号之间的数字、空格和逗号的任意组合,允许尾随空格和 CR);如果任何此类行匹配,则仅保留方括号之间的内容 (\1
, ,对应于之间的内容\(...\)
在正则表达式中)在打印之前(:p
)- sed 将输出如下所示的行:
8541, 931, 0, 0
- sed 将输出如下所示的行:
awk
使用由 0 个或多个空格包围的逗号 (-F ' *, *'
) 作为字段分隔符;$1
对应于第一列(例如8541),$2
到第二个等等。缺失的列计为值0
- 在最后,
awk
除累加器sum2
等处理的记录数量,NR
- 在最后,
gawk
一举完成所有事情;它将首先测试每一行是否与上一个示例中传递的相同正则表达式匹配sed
(除了与sed
,awk
不需要\
在划定区域或兴趣的圆括号中)。如果该行匹配,圆括号之间的内容将以 a[1] 结束,然后我们使用相同的分隔符(由任意数量的空格包围的逗号)将其分割并使用它进行累加。我介绍了cnt
而不是继续使用NR
因为处理的记录数NR
可能大于相关记录的实际数量(cnt
) 如果并非所有行都采用以下形式INFO ... [...comma-separated-numbers...]
, ,情况并非如此sed|awk
自从sed
保证所有线路都传递到awk
是相关的。
在 Solaris 上使用 nawk 或 / usr / xpg4 / bin / awk 。
awk -F'[],]' 'END {
print s/NR, t/ct
}
{
s += $(NF-3)
if ($(NF-1)) {
t += $(NF-2)
ct++
}
}' infile
使用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