awk命令之通过在awk中使用数组及for循环来统计不同IP出现
https://www.imzcy.cn/2387.html
查看服务器 正在进行的链接
netstat -antp | grep 80 | grep ESTABLISHED -c
cat 202008/*.log |grep 'GET' |awk '{print 1}'|awk '{a[$1]+=1} END {for(i in a){print a[i],i}}'|sort -n
最新没事通过阿里云手机app查看本站CDN使用状态,发现CDN统计信息中返回码4xx的占比一直持续很多,基本30%左右,有时候还飙到100%了快。通过查看nginx日志,发现有很多IP在刷站点上根本不存在的地址,所以有大量404出现。于是想统计下看哪些IP访问404页面比较多,将其加到黑名单,限制访问本站点。既然要统计不同IP出现次数了,肯定要用awk来比较方便了。记得之前有用awk来统计linux下不同状态连接数。但是真用起来突然发现不知道怎么用了。。。时间真是把杀猪刀⊙﹏⊙||| 又查了半天资料才给搞好,,于是想着还是记录下笔记吧!
- 示例文件如下
为了方便讲解,这里就不拿nginx日志来做演示了,只给出一个包含不同IP的文件(效果都是一样的,处理nginx日志只是提前将状态码404的行都筛选出来,然后再统计IP那一列出现次数而已,例如:awk '/<404>/{print $1}')。
[root@imzcy ~]# cat test.txt
192.168.1.3
192.168.1.3
192.168.1.2
192.168.1.6
192.168.1.2
192.168.1.3
192.168.1.6
192.168.1.3
192.168.1.6
192.168.1.2
192.168.1.2
192.168.1.2
192.168.1.2
192.168.1.2
192.168.1.2
192.168.1.2
192.168.1.6
192.168.1.6
192.168.1.6
192.168.1.6
[root@imzcy ~]#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@imzcy ~]# cat test.txt
192.168.1.3
192.168.1.3
192.168.1.2
192.168.1.6
192.168.1.2
192.168.1.3
192.168.1.6
192.168.1.3
192.168.1.6
192.168.1.2
192.168.1.2
192.168.1.2
192.168.1.2
192.168.1.2
192.168.1.2
192.168.1.2
192.168.1.6
192.168.1.6
192.168.1.6
192.168.1.6
[root@imzcy ~]#
- 我们使用如下awk语句即可统计出不同IP出现的次数了
[root@imzcy ~]# cat test.txt |awk '{a[1]+=1;} END {for(i in a){print a[i]" "i;}}'
9 192.168.1.2
4 192.168.1.3
7 192.168.1.6
[root@imzcy ~]#
- 详细执行过程解释
上面所使用的 '{a[1]+=1;} 和 {for(i in a){print a[i]" "i;}} ,因为只有在前面部分代码段将我们指定的最后一个输入文件的最后一行处理完之后才会处理END后边的内容。
3.1 我们就先讲 {a[$1]+=1;} 这部分
这部分相当于定义了一个数组a,元素名为指定的输入文件的$1的值,元素值为+=1 (这么讲可能不怎么精确,但大致是这个意思)。在处理test.txt文件的时候,因为awk是按行执行的,根据上面test.txt文件中所示的内容。
• 第一行的$1的值为192.168.1.3,就相当于给数组a定义了一个新的元素192.168.1.3的值为1,然后向下处理第二行数据。
• 第二行的$1的值还是192.168.1.3,因为数组a已经存元素192.168.1.3,此时192.168.1.3+=1,其值就等于2了。然后继续向下处理第三行数据。
• 第三行的$1的值为192.168.1.2,相当于给数组a又定义了一个新的元素192.168.1.2的值为1,然后向下处理第四行数据。
• ...
• 一直到处理完最后一行数据,此时开始使用END模式后边的代码来处理数据。
3.2 接下来讲 {for(i in a){print a[i]" "i;}} 这部分
这部分定义了一个for循环,从前面定义的数组a中读取所有的元素名。并打印出来元素的值及加空格分割打印了元素名。 相当于for i in a; do echo "a[i] i"; done 假设数组a的元素名是按照创建先后顺序来排序(根据实际输出结果,可以看出其实是相当于根据大小排序了的,我们这里先这样讲),那么数组a里面会有3个元素192.168.1.3、192.168.1.2、192.168.1.6。
• 第一次循环,i = 192.168.1.3 ,此时print a[i],相当于打印了数组a元素为192.168.1.3的值(也就是+=1的次数),然后" "分割,打印了i的值 192.168.1.3。
• 第二次循环,i = 192.168.1.2 ,此时print a[i],相当于打印数组a元素为192.168.1.2的值,然后" "分割,打印了i的值,192.168.1.2。
• 第三次循环,i = 192.168.1.6 ,此时print a[i],相当于打印数组a元素为192.168.1.6的值,然后" "分割,打印了i的值,192.168.1.6。
• ...
• 一直到循环结束,退出awk执行。