awk二维数组实例一则

好久没有写过比较复杂的 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.