使用awk对某列去重并且可保留其他列

时间:2022-06-26 16:07:17

同事说需要统计日志中按url的path去重之后的结果,结果中要保留参数。相同url不同参数的,只保留第一行。

理论上各种命令都是流式处理,一行一行的处理。每道命令都相当于一个过滤器。比如你要按某列去重,则事先把数据cut到只剩你需要去重的这一列。对url中的path做sort+uniq。

但是这样会导致参数都没了。所以不能用这个。


最后成功的命令如下:

cut -f  7  -d  " "  access. 2018 - 01 - 11 .log | sed  's/?/ /'  | awk  '!a[$1]++{print}'

解释一下

a[$1]是$1的hash值,在这里是被作为变量名了,初始值为0,++表示在本行执行完毕之后+1

那么awk走到第一行时,该变量刚刚初始化则是0,取反后为1,即为true,所以第一行会被print

第二行如果跟第一行的a[$1]一样,则该变量的值已经在上一行被+1了,所以值不是0了,而是1。则取反之后值为0,即为false,则本行不print

第三行如果还一样,则变量值为2,由于在C中,只要不是0就是真,所以第三行变量值取反为false,不print

---------------------------------------------------

后来同事又提了更过分的要求,想要把以上命令再结合ssh,从跳板机就直接远程获取到处理好的数据并写入文件,这样不用一个个机器去登。

一开始是这样写的,会报错。

ssh xxxxx.beta.cn0  "zgrep -e 'HTTP/1.0\" \(2\|3\)0' /xxxxxx/logs/access.2018-01-08.log | cut -f 7 -d ' '| sed 's/?/ /' | awk '!a[$1]++{print}' "  >> valid_access.log
报错如下:
-bash: !a[$ 1 ]++: event not found
最后换了个思路,把awk放到本地执行,就解决了:
ssh xxxxx.beta.cn0  "zgrep -e 'HTTP/1.0\" \(2\|3\)0' /xxxxxx/logs/access.2018-01-08.log | cut -f 7 -d ' '| sed 's/?/ /' "  | awk  '!a[$1]++{print}'  >> valid_access.log