经grep日志后得到的数据格式如下:
} . [debug][2019-05-20 00:00:00] SendDataStyled:{ "cmd" : 0001, "innings" : "6189269620_0007", "players" : [ { "al_board" : 1, "al_win" : 1, "alter_exp" : 0, "alter_money" : 10, "uid" : 34329592 }, { "al_board" : 1, "al_win" : 0, "alter_exp" : 0, "alter_money" : -26, "uid" : 13416009 }, { "al_board" : 1, "al_win" : 1, "alter_exp" : 0, "alter_money" : 16, "uid" : 41165640 } ], "roomid" : "6189269620", "vid" : 1205 }
希望将一条原始数据处理后,生成若干条以下格式的数据:
time al_win alter_money uid roomid
由于很久没有用过awk了,先复习了一遍语法做了些小实验才正式开始。经过整理后的思考路径如下:
- 确定分隔符。 原本是想用空格,“[”, “]”,三个符号做分隔的,实验后发现由于打印格式的问题,很多字段前面都有很多个空格,这样不太好数需要的字段编号。所以最后分隔符定为中括号,双引号,冒号,逗号。
- 确定所需字段分隔后的编号。 根据分隔符数一下,然后通过命令行验证一下就可以了。这个还是很容易且快的。
- 思考如何组织并输出数据。 基本想法是除了要重复用到的time和roomid保存一下,其它字段是拿到后就直接打印,读取uid之后,打印uid及roomid就进行换行。有点困难的是roomid的读取,因为awk是按行顺序处理的,一个uid打印一行数据的话就等不到读后面的roomid了。幸好发现在数据innings里实际上隐含着roomid,拿到innings后用“_”截取前面一段就可以了。
- 实操。
整个路径看起来是挺简单的,实际上用了我好几个小时才写出了完整可用的awk。特此记录下遇到的问题及得到教训。
- 尽量用awk脚本写,不要在命令行直接写。 也是因为只稍微复习了下语法就开始的原因,不知道可直接写在文件里调用,在命令行写极易出错,不易阅读,而且修改起来相当痛苦。
- 自定义变量的生命周期。 不说太深,只说现今用到的。awk我们日常用到的自定义变量基本都是全局变量,直接使用就可以了。只是要注意下自己的程序应当在什么时候对变量进行初始化。如果只初始化一次,就在BEGIN里写;如果需要在具体的行里多次初始化,在BEGIN不用初始化也可以直接用。
- 变量的使用。 也是由于复习不到位的问题,没有注意到使用变量时不需要使用$,比如要打印NF变量,直接print NF即可。因为这个小问题,走了不少弯路,深刻的教训。
- printf 的随意性。 如果没有定义具体的打印格式,printf后接空格,再接要打印的变量名或字段就可以了,用逗号或空格分隔都可以。要打印的所有变量可以用小括号包起来,也可以不包起来。不得不说,真的是强!
- 定义分隔符在命令行和脚本里的不同。 以上文确定的分隔符为例,在命令行应该写作 -F '[][":,]' , 在脚本里则应该在BEGIN里写成 FS="[][":,]"; 。
最后,贴上写完的脚本:
source.awk
BEGIN { FS="[][":,]"; } { if($4~/2019-/) a=$4":"$5":"$6; else if($2~/innings/) split($5, b, "_"); else if($2~/al_win/) printf a $4; else if($2~/alter_money/) printf $4; else if($2~/uid/) printf "%s %s ", $4, b[1]; }
直接使用 awk -f source.awk data.log > res.txt 即可。
另附参考的awk基础知识: