我有一个如下所示的文本文件(events.dat)(注意仅显示摘录)
RepID12 01/01/2010 20:56:00 S10
RepID12 01/01/2010 20:56:00 S03
RepID20 01/01/2010 20:56:00 S17
RepID33 01/01/2010 20:56:00 S02
RepID33 01/01/2010 20:56:00 S18
RepID38 01/01/2010 20:56:00 S11
RepID39 01/01/2010 20:56:00 S20
RepID26 02/01/2010 01:39:00 S20
RepID29 02/01/2010 01:39:00 S16
RepID29 02/01/2010 01:39:00 S03
RepID22 02/01/2010 01:39:09 S01
RepID26 02/01/2010 01:39:09 S02
RepID40 02/01/2010 01:39:18 S02
RepID38 02/01/2010 01:39:09 S05
RepID31 02/01/2010 01:39:09 S06
RepID31 02/01/2010 01:39:09 S08
RepID09 02/01/2010 01:39:09 S09
RepID23 02/01/2010 01:39:18 S09
RepID19 02/01/2010 01:40:09 S09
RepID21 02/01/2010 01:40:18 S09
RepID28 02/01/2010 01:40:27 S09
RepID43 02/01/2010 01:40:09 S14
等等,涵盖总时间跨度为 48 小时的事件。我只想打印每分钟发现超过 60 个事件时的行。
例如,使用此命令,我可以计算 1 分钟内有多少个事件:
grep "02/01/2010 01:39" events.dat | wc -l
这将返回 60(例如),这应该是每分钟的最大事件数。
我怎样才能做同样的事情,但检查整个 48 小时内的每一分钟并仅打印出发现超过 60 个事件/分钟的行?提前谢谢
答案1
理想情况下,您只想尝试仅处理该文件一次,并将尽可能少的内容存储在内存中。在 中awk
,你可以这样做:
awk -v n=60 '
{
t = $2 substr($3, 1, 5);
if (t == last_t) {
if (++lines > n)
print
else
if (lines == n)
print saved $0
else
saved = saved $0 RS
} else {
saved = $0 RS
lines = 1
last_t = t
}
}' < your-file
这种方法的一些优点:
- 这是面向流处理的。输入一到来就立即处理,并尽快发出输出(一旦看到第 60 行)。这使得对实时输出进行后处理成为可能(就像在 a 上一样
tail -fn +1 log_file
)。 - 它只运行一个命令 (
awk
) 的一次调用,因此将尽可能高效。相反的极端是循环运行多个命令。 shell 脚本中成本最高的事情通常是分叉和执行命令。优化意味着尽可能减少这种情况。 - 我们在内存中最多只存储 60 行,因此内存使用量将受到限制(假设行本身的大小受到限制)。
awk
代码可以变得非常清晰且不言自明。现在,如果尺寸很重要,您也可以将其缩短并放在一行上,例如awk '{t=$2substr($3,1,5);if(t==l){if(++i>n)print;else if(i==n)print s$0;else s=s$0RS}else{s=$0RS;i=1;l=t}}' n=60 file
答案2
这不是最有效的解决方案,但您可以首先计算每分钟的事件数,然后当计数 >= 60 时,为这些分钟中的每一分钟 grep 文件。
sort -k 2,3 your_log_file \
| uniq -c -s 8 -w 16 \
| while read count _ date time _; do
[ "$count" -ge 60 ] && grep -F " $date ${time%:*}" your_log_file
done
笔记:
- 在上面的基本示例中,我首先按时间顺序对文件进行排序
- 如果这是您唯一感兴趣的信息,前两行将为您提供每分钟的事件数。
如果您的文件充满了事件,您很可能最终会grep
对其执行大量操作。更好的解决方案是按顺序读取日志文件,并记住最后一分钟的行。当您到达下一分钟时,如果这些行的数量大于 60,则打印这些行。有关此类解决方案,请参阅 Stéphane 的答案。
答案3
通过这样的方法,您可以隔离可用的分钟数:
root@debian:# awk -F" " '{print $2" "$3}' b.txt |cut -f1-2 -d: |uniq
01/01/2010 20:56
02/01/2010 01:39
02/01/2010 01:40
02/01/2010 20:56
然后您可以使用这些值分配一个数组
修改后的代码:
readarray -t stamps < <(awk -F" " '{print $2" "$3}' b.txt |cut -f1-2 -d: |uniq)
for stamp in "${stamps[@]}";do
ev=$(grep "$stamp" b.txt |wc -l)
echo "In $stamp found $ev events "
#if [ "$ev" -gt 60 ]; then
# do the stuff
#fi
done
输出:
In 01/01/2010 20:56 found 7 events
In 02/01/2010 01:39 found 11 events
In 02/01/2010 01:40 found 4 events
In 02/01/2010 20:56 found 7 events
答案4
awk '{ print $2 " " $3 }' < input \
| cut -c1-16 \
| sort \
| uniq -c \
| awk '{ if ($1 > 60) print $2 }'
即获取日期和时间字段,去掉秒数,对结果进行排序(注意:如果您的日期采用 ISO 格式,效果会更好),找到每个唯一日期/时间组合的计数,然后使用计数 > 打印它们60