我正在安排一个每分钟运行一次的cron,并为每分钟提供REJECT
字数。我的文件被连续记录并且为了避免冗余读取,我在使用tail -n + lastTimeWC运行脚本时存储了上次读取的行。但是我如何计算每分钟REJECT的数量。样本输入:
20170327-09:15:01.283619074 ResponseType:REJECT
20170327-09:15:01.287619074 ResponseType:REJECT
20170327-09:15:01.289619074 ResponseType:REJECT
20170327-09:15:01.290619074 ResponseType:REJECT
20170327-09:15:01.291619074 ResponseType:REJECT
20170327-09:15:01.295619074 ResponseType:REJECT
20170327-09:15:01.297619074 ResponseType:REJECT
20170327-09:16:02.283619074 ResponseType:REJECT
20170327-09:16:03.283619074 ResponseType:REJECT
20170327-09:17:02.283619074 ResponseType:REJECT
20170327-09:17:07.283619074 ResponseType:REJECT
预期产出:
9:15 REJECT 7
9:16 REJECT 2
9:17 REJECT 2
Update1 :(使用Ed Morton的回答)
#!/usr/bin/bash
while :
do
awk -F '[:-]' '{curr=$2":"$3} (prev!="") && (curr!=prev){print NR, prev, $NF, cnt; cnt=0} {cnt++; prev=curr}' $1
sleep 60
done
这个脚本在60秒后不断给我输出。但它应该只添加新的时间戳添加到日志文件($!)
假设9:18被添加,然后它应该开始包括答案(不再是9:15到9:18)
答案 0 :(得分:1)
您可以在Awk
中执行此操作,方法是将分钟值作为索引进行散列,并将假设状态不会每分钟更改,如下所示,
awk -F'[-:]' '{unique[$2":"$3]++; uniqueValue[$2":"$3]=$NF; next}END{for (i in unique) print i,uniqueValue[i],unique[i]}' file
09:15 REJECT 7
09:16 REJECT 2
09:17 REJECT 2
答案 1 :(得分:1)
不打印最后一次计数,因为该时间戳可能不完整,只需在此之前打印计数:
$ awk -F '[:-]' '{curr=$2":"$3} (prev!="") && (curr!=prev){print prev, cnt, $NF; cnt=0} {cnt++; prev=curr}' file
09:15 REJECT 7
09:16 REJECT 2
如果你真的想要打印最后一个,那么只需在END部分添加一个打印:
$ awk -F '[:-]' '{curr=$2":"$3} (prev!="") && (curr!=prev){print prev, $NF, cnt; cnt=0} {cnt++; prev=curr} END{print prev, $NF, cnt}' file
09:15 REJECT 7
09:16 REJECT 2
09:17 REJECT 2
但我想你不得不放弃那可能部分的结果,那么重点是什么?
请注意,您不必将所有结果存储在数组中,然后在END部分中打印它们,只需在每次时间戳更改时打印它们。除了不必要地使用内存之外,将所有结果存储在数组中然后使用in
在END部分中使用循环打印它们的解决方案将以随机(实际哈希)顺序打印输出,而不是顺序时间戳出现在你的输入中(除非有时候运气不好)。
而不是存储输入文件的行数(当时间戳结果在脚本调用之间被分割时,这会导致错误的结果,并且无法使用logrotate
或类似的截断日志文件,因为它& #39; s变长/变老),存储分析的最后时间戳并在当前迭代之后开始,例如用cron做相同的事情:
while :
do
results=( $(awk -F '[:-]' -v last="$lastTimeStamp" '{curr=$2":"$3} curr<last{next} (prev!="") && (curr!=prev){print prev, $NF, cnt; cnt=0} {cnt++; prev=curr}' file) )
numResults="${#results[@]}"
if (( numResults > 0 ))
then
printf '%s\n' "${results[@]}"
(( lastIndex = numResults - 1 ))
lastResult="${results[$lastIndex]}"
lastTimeStamp="${lastResult%% *}"
fi
sleep 60
done
或者如果您想使用行号,那么您可以执行tail
,而不是使用wc -l
来获取文件的长度(包括当前时间戳,您不打印可能不完整的结果for),让awk打印与每个时间戳关联的最后一行之后的行号:
$ awk -F '[:-]' '{curr=$2":"$3} (prev!="") && (curr!=prev){print NR, prev, $NF, cnt; cnt=0} {cnt++; prev=curr}' file
8 09:15 REJECT 7
10 09:16 REJECT 2
并将其剥离以在打印结果之前保存最后一个值。最后一个值是您在下一次迭代时所做的tail -n +<startLineNr> | awk '...'
。
顺便说一下,您没有在示例输入中向我们展示这一点,但如果您的日志文件包含不包含REJECT的行并且您希望忽略这些行,则只需在awk脚本的开头添加$NF!="REJECT"{next}
即可。
答案 2 :(得分:0)
包括REJECT过滤器,日期和流版本(内存中没有数组,只是最后一个计数器和日期参考
awk -F '-|:..[.]|pe:' '$NF=="REJECT"{if(L==$1"-"$2)C++;else{print L" REJECT " C;C=1;L=$1"-"$2}}END{print L" REJECT " C}' YourLog
包括评论中提到的“不退缩相同信息”(只需在代码中查看重读的“最后知道时间”)
CFile=Counter.log
# just to insure there is a counter file (could be empty) for awk input
touch ${CFile}
awk -F '-|:..[.]|pe:' -v CF="${CFile}" '
FNR==NR {
if( CF == FILENAME) {L=$0;next}
}
# dont treat element before
# (so we include last know time that was maybe still logging at last cycle)
L > ( $1 "-" $2 ) { next }
$NF=="REJECT" {
if(L==$1"-"$2)C++
else {
print L" REJECT " C;C=1;L=$1"-"$2
}
}
END{
print L" REJECT " C
# write new counter info
print L > CF
}
' ${CFile} YourLog