前些时间去面试,其中又这么一道题:写一个脚本,统计一下apache日志中IP出现频率最高的前五个。
当时就懵了。apache日志,大爷的,好歹给个例子,让我看看apache日志长什么样子。
没办法,只得动用我正则表达式的储备了,于是,尝试使用正则表达式来解决问题。
简略分析下,分为这么几步:
首先,把IP地址过滤出来,可以使用
grep "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}"
然后,使用sed把包含IP地址的给搞出来,这里肯定要用到分组
sed '/.*\("[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}"\).*/\1/'
最后是,统计每个IP地址出现的频率,并把IP地址和频率打印出来
awk '{s[$0]++}END{for(i in s)print i,s[i]}'
还有一步,就是按照频率逆序排列,并取前五行
sort -k2nr | head -5
把语句连起来,组合调试一下
cat File_Name | grep "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" | \
sed 's/.*\([0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\).*/\1/' | \
awk '{s[$0]++}END{for(i in s)print i,s[i]}' | \
sort -k2nr | head -5
然后,编写一个文件,名字就叫做 test.lst ,里面随便写点东西:
10.2.3.4 dsjakld
10.2.3.4 dsjakld
10.2.3.5 dsjakld
10.2.3.4 dsjakld
10.2.3.4 dsjakld
10.4.3.4 dsjakld
10.2.3.1 dsjakld
10.2.3.4 dsjakld
10.2.3.4 dsjakld
10.4.3.1 dsjakld
10.2.3.4 dsjakld
10.2.3.4 dsjakld
10.2.2.1 dsjakld
10.2.3.4 dsjakld
10.4.3.4 dsjakld
把语句帖进来执行试试看:
[root@bogon ~]# cat test.lst | grep "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" | \
> sed 's/.*\([0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\).*/\1/' | \
> awk '{s[$0]++}END{for(i in s)print i,s[i]}' | \
> sort -k2nr | head -5
0.2.3.4 9
0.4.3.4 2
0.2.2.1 1
0.2.3.1 1
0.2.3.5 1
好奇怪,IP地址明明是 10开头,为毛变成了 0.
仔细想想,知道了,是 * 惹的祸,改造下:
[root@bogon ~]# cat test.lst | grep "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" | \
> sed 's/[^0-9]*\([0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\)[^0-9]*/\1/' | \
> awk '{s[$0]++}END{for(i in s)print i,s[i]}' | \
> sort -k2nr | head -5
10.2.3.4 9
10.4.3.4 2
10.2.2.1 1
10.2.3.1 1
10.2.3.5 1
似乎没问题了。也许还潜藏着别的BUG,不过就目前而言,是瞒着要求了。
先这么着的,略微优化一下,或者说改变一下:
cat test.lst | grep "[[:digit:]]\{1,3\}\.[[:digit:]]\{1,3\}\.[[:digit:]]\{1,3\}\.[[:digit:]]\{1,3\}" | \
sed 's/[^[:digit:]]*\([[:digit:]]\{1,3\}\.[[:digit:]]\{1,3\}\.[[:digit:]]\{1,3\}\.[[:digit:]]\{1,3\}\)[^[:digit:]]*/\1/' | \
awk '{s[$0]++}END{for(i in s)print i,s[i]}' | \
sort -k2nr | head -5
再一想,我好想没必要把那些行先过滤出来,直接替换就是了:
cat test.lst | sed 's/[^[:digit:]]*\([[:digit:]]\{1,3\}\.[[:digit:]]\{1,3\}\.[[:digit:]]\{1,3\}\.[[:digit:]]\{1,3\}\)[^[:digit:]]*/\1/' | \
awk '{s[$0]++}END{for(i in s)print i,s[i]}' | \
sort -k2nr | head -5