shell统计日志文件实现指定格式Top10

背景:
无论oracle、hive还是spark等都有实现该功能的函数,但我们如何利用 linux-shell命令来处理用户访问日志实现top10功能呢?这相对比较偏底层了,了解下很有好处~


一、需求

日志文件: haha.txt
字段分隔符为:\t
字段说明: 时间 用户id 访问的url

日志文件部分内容如下:

2018-05-17 00:00:06 99986406000 https://tvax2.huhuimg.cn/crop.0.0.1242.1242.50/006wGPSAly
2018-05-17 00:00:06 99986406000 https://tvax2.huhuimg.cn/crop.0.0.1242.1242.50/006wGPSAly
2018-05-17 00:00:04 99986404000 https://tvax2.huhuimg.cn/crop.0.0.960.960.50/ab12aff8ly8f
2018-05-17 00:00:05 99986405000 https://tvax2.huhuimg.cn/crop.0.0.960.960.50/ab12aff8ly8f
2018-05-17 00:00:11 99986411000 https://tvax3.huhuimg.cn/crop.0.0.512.512.50/005IxHV9ly8f
2018-05-17 00:00:06 99986406000 https://tvax1.huhuimg.cn/crop.0.0.960.960.50/609H3d2bly8f
2018-05-17 00:00:05 99986405000 https://tvax2.huhuimg.cn/crop.0.9.493.493.50/88d9a6f1ly8f
2018-05-17 00:00:00 99986400000 https://tva2.huhuimg.cn/crop.0.0.640.640.50/7270cH6Hjw8fb
2018-05-17 00:00:00 99986400000 https://tva2.huhuimg.cn/crop.0.0.640.640.50/7270cH6Hjw8fb
2018-05-17 00:00:00 99986400000 https://tvax2.huhuimg.cn/crop.4.0.742.742.50/006MNIDXly8f
2018-05-17 00:00:03 99986403000 https://tvax4.huhuimg.cn/crop.0.0.843.843.50/6c98bc61ly8f
2018-05-17 00:00:05 99986405000 https://tvax4.huhuimg.cn/crop.0.0.843.843.50/6c98bc61ly8f
2018-05-17 00:00:01 99986401000 https://tvax3.huhuimg.cn/crop.0.0.1125.1125.50/750031H5ly

要求输出字段以 \t 分隔:

1. 请给出访问用户数最多的 top 10 url。输出格式: 序号 url
2. 请给出top 10 url的最后一次访问时间。输出格式 url 最后一次访问时间


二、初步分析

1.我们拿到目标日志文件,先看看文件类型:

file haha.txt

这里写图片描述
file命令 查看文件类型,为了区分windows和linux文本,输出内容:haha.txt: ASCII text,发现是linux文本。
如果我们在windows下创建文件file.txt,内容完全复制haha.txt,将其发送到linux中,执行命令file file.txt,输出内容将会为:file.txt: ASCII text, with CRLF line terminators。(CRLF:回车换行符)这是因为windows中行结束符和linux中行结束符不同,windows中是\r\n,linux中是\n。测试下:

[root@VM_0_17_redhat ~]# wc -m haha.txt 
2269648 haha.txt
[root@VM_0_17_redhat ~]# wc -m file.txt 
2289648 file.txt

发现file.txt比haha.txt多了20000个字符,正好对应windows下多余的换行符!

注:
1.unix2dos命令:将具有unix风格的格式文件转化为具有window下的格式文件。
2.dos2unix命令:将具有windows风格的格式文件转化为unix下的格式文件。

2.查看目标文件内容中的具体字符

cat -A haha.txt | head

这里写图片描述

我们平时可能cat用的相对较多,但加上-A就用的很少了,该参数可以显示所有不可见的字符!可以看到tab分隔符为^I,换行符为$

关于分隔符:
tab做分隔是最棒的,可以避开很多坑。因为各种数据字段的内容不一般不会有tab健。其他的字符容易撞车。而用不可见字符也可以,但是就自己找苦吃,有自虐症的除外。。


三、需求-1解析

需求:请给出访问用户数最多的 top 10 url。输出格式: 序号 url

该需求表面上是对url进行次数统计再top10排序,但有一个隐藏点:同一个用户对同一个url的访问只记一次!也就是先要对用户id+url进行联合去重!

这里有两种答案,第一种是主要利用awk,第二种是用uniq、sort等:

第一种:

awk -F'\t' '!a[$2,$3]++' haha.txt | awk 'BEGIN {FS="\t";OFS="\t"}{s[$3] += 1}END{ for(i in s){ print s[i],i} }' 
| sort -nr | head | awk 'BEGIN {OFS="\t"}; {print NR,$NF}'>shell_answer1

阶段分析:
1.awk命令:指定tab为分隔符,通过awk的patten:’!a[$2,$3]++‘,以第二、三列为数组下标,进行联合去重!具体原理见我上篇博客对awk的介绍~

2.awk命令:通过BEGIN指定 输入、输出分隔符都为’\t’,再对第三列单独进行去重。最后在END中,打印两列(第一列为数组value,其实就是出现重复记录的数量,第一次出现的行s[$3]为0,!后为true,成功输出;s[$3] += 1后,value变为1,之后每出现一次value加1,即相当于出现次数)

3.sort命令:-nr 按数值降序排列,没指定分隔符分段的原因是出现次数本就在第一列,按这个排序没毛病

4.head命令:默认取前十展示

5.awk命令:主要为了按要求输出,第一列为行数(NR),1-10,第二列为最后一个字段($NF,即url列,此处用$2也可以)

第二种:

cat haha.txt | awk -F'\t' '{print $2" "$3}' | sort | uniq | awk '{print $2}' | sort -n | uniq -c | sort -nr | head | awk 'BEGIN {OFS="\t"}; {print NR,$1,$2}'  (这个输出了访问top10url的用户数)

这种写法比较好理解!


四、需求-2解析

需求:请给出top 10 url的最后一次访问时间。输出格式 url 最后一次访问时间

这个对比上面主要是多了一个最后一次访问时间,这个极大增加了复杂性!我们选择通过join的方式来完成该需求!

先统计url的最后一次访问时间,形成url、该url最后一次访问时间两字段的模拟字典表(文件):

awk 'BEGIN{FS="\t";OFS="\t"} {if($1>a[$3]) {a[$3]=$1;b[$3]=$3"\t"$1}} 
END{for(i in b) print b[i]}' haha.txt | sort > shell_answer2mid

此处又是通过awk的数组实现最后一次访问时间的计算!

if($1>a[$3]) 其中$1为访问时间,a[$3]为下标为$3即url下标的数组值,
刚进if条件时,a[$3]还未赋值,为空,此时$1有值,所以首次比较必为true。然后进入到{a[$3]=$1;b[$3]=$3}里,做了两件事:
第一是将该行的第一个字段-即访问时间赋值给数组a[$3];这样做为了干啥呢?这又要结合前面的if条件来思考了:如果不同url进入,a[url]必为空,然后将访问时间赋值给a[url];如果相同的url进入,则a[$3]已经被赋值过了,此时就要把当前行的$1时间和之前赋值过的a[$3]时间作比较了,如果当前行时间大于之前的时间,则为true,将最新的大时间再次赋值给a[$3],这样就实现了:相同url对应的时间筛选出了最大值!
第二是将$3(url)赋值给数组b[$3],这是为了后面END打印url专用

这样就实现了查询中间表:
这里写图片描述

然后我们这需要把第一个需求的top10 url文件(shell_answer1)和第二个需求的中间表(shell_answer2mid)join就行:

awk 'BEGIN{FS="\t"; OFS="\t"} NR==FNR{a[$1]=$2;} NR!=FNR && $2 in a{print $2,a[$2]}' 
shell_answer2mid shell_answer1 > shell_answer2

NR==FNR{a[$1]=$2;}读取shell_answer2mid文件中的记录,将每个url的timemax都赋值给数组a[url],这样后面就可以通过url取得timemax值了!
然后NR!=FNR && $2 in a{print $2,a[$2]},读取shell_answer1文件中的记录,并且需要同时满足$2 in a,即shell_answer1中的$2(url)该下标在a数组中必须存在对应值(a[url])(其实应该100%满足)

猜你喜欢

转载自blog.csdn.net/Abysscarry/article/details/81271546