好久没有写过比较复杂的 awk 命令了,今天刚好遇到了一个实际问题,简单记录下。
日志文件的内容格式如下:
<965> : 2017-02-20 10:16:46 1.1.1.2
<965> : 2017-02-20 10:16:57 1.1.1.2
<968> : 2017-02-21 12:12:46 1.1.1.1
<967> : 2017-02-21 12:12:58 1.1.1.1
<987> : 2017-02-21 12:13:24 1.1.1.2
<969> : 2017-02-22 01:05:43 1.1.1.2
<988> : 2017-02-22 01:19:39 1.1.1.3
需要找出每天的去重 ip 个数。如上面的日志的话,正确输出应该是:
2017-02-20: 1
2017-02-21: 2
2017-02-22: 2
方法一
对方急着要结果,所以一开始用的是最笨的方法,写脚本:
#!/bin/bash
DATE=$(awk '{print $3}' www.log | sort | uniq)
for d in ${DATE}; do
echo -n "${d} "
lines=$(/bin/grep -E "${d}" www.log | /bin/grep -Po '\d+\.\d+\.\d+\.\d+' | sort | uniq | wc -l)
echo "${lines}"
done
方法二:
想起了可以用 awk 的二维数组:
awk '{a[$3" ", $5]++}END{for(i in a){print i}}' www.log
如果是上面的例子,结果是这样的
2017-02-21 1.1.1.1
2017-02-21 1.1.1.2
2017-02-22 1.1.1.2
2017-02-22 1.1.1.3
2017-02-20 1.1.1.2
上面只是做了一维去重,然后再加上一个 awk 再去重:
awk '{a[$3" ", $5]++}END{for(i in a){print i}}' www.log | awk '{a[$1]++}END{for(i in a)print i,a[i]}'
得到的结果跟方法一是一致的。还不赖,起码一行搞掂,不用写脚本了。
方法三
上面的方式还是比较 low 的,用到了管道。后来找了下之前的笔记,重新复习了 awk 的二维数组,终于撸出了直接一条 awk 命令:
awk '{a[$3,$5]++}END{for(i in a){split(i,idx,SUBSEP);b[idx[1]]++};for(i in b){print i,b[i]}}' www.log
PS:二维数组需要用 split(i,idx,SUBSEP)
分割单独取得下标,如上面的例子就是分割后存在了 idx
数组里,然后用 idx[1]
获取一级下标,idx[2]
获取二级下标。
That's all.