Shell中使用sort命令多分隔符排序日志文件报错的问题咨询
问题描述
我手头有个日志文件access-2014-09-04.log,里面的内容是这样的:
192.168.130.175 2014-09-04 10:25:01 /index.html
192.168.138.244 2014-09-04 11:23:00 /index.html
192.168.138.244 2014-09-04 10:29:37 /products.html
192.168.138.244 2014-09-04 11:22:49 /products.html
192.168.83.173 2014-09-04 10:05:17 /products.html
192.168.130.175 2014-09-04 12:24:24 /products/004.html
192.168.130.175 2014-09-04 10:09:13 /products/296.html
192.168.130.175 2014-09-04 11:01:20 /products/296.html
192.168.83.173 2014-09-04 12:19:55 /products/560.html
我想实现的排序逻辑是:先按IP地址的数字大小排序,要是IP相同的话,就按第四个字段(网页路径)的字母顺序排。于是我试了这条命令:
sort -t. -k1,2 -k3,4 -n -t $'\t' -k4 access-2014-09-04.log
结果却报错了:sort: incompatible tabs。有没有大佬能帮忙看看怎么解决?
错误原因解析
其实你这命令里踩了个sort的小坑——你重复用了两次-t选项来指定不同的分隔符(.和制表符\t),但sort根本不支持同时设置多种分隔符,它没办法判断到底该用哪个来拆分字段,自然就抛出“incompatible tabs”的错误了。
可行解决方案
针对你的排序需求,我给你整理了三种靠谱的解决办法,你可以根据自己的习惯选:
方法一:两次排序(利用稳定排序特性)
sort默认是稳定排序,我们可以先按次要关键字(路径)排,再按主要关键字(IP)排,这样相同IP的条目会保留之前按路径排序的结果:
# 先按路径字典序排序,再按IP数字排序(-s保证稳定排序) sort -t$'\t' -k4 access-2014-09-04.log | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n -s
这里第一次sort用制表符拆分字段,按第四个字段(路径)排序;第二次用.拆分IP的四个分段,每个分段都按数字排序,-s参数确保稳定排序,不会打乱之前路径的排序结果。
方法二:直接定位IP字段的字符范围
既然整个日志行是用制表符分隔的,我们可以把IP当作第一个字段,直接指定这个字段里的字符范围来做数字排序,不用切换分隔符:
sort -t$'\t' -k1.1,1.3n -k1.5,1.7n -k1.9,1.11n -k1.13,1.15n -k4 access-2014-09-04.log
解释一下:
-t$'\t':全局用制表符拆分字段,整个行分成IP、日期、时间、路径4个字段-k1.1,1.3n:对第一个字段(IP)的第1-3个字符(比如192)做数字排序-k1.5,1.7n:对IP的第5-7个字符(比如168)做数字排序,以此类推处理后两段-k4:最后按第四个字段(路径)做字典序排序
方法三:用版本排序(最简洁)
如果你用的是GNU版本的sort(大部分Linux系统都是),可以用-V选项做版本排序,它能自动识别IP这种类似版本号的格式,直接正确排序:
sort -t$'\t' -k1V -k4 access-2014-09-04.log
这个命令就很直观了:-k1V对第一个字段(IP)做版本排序,-k4按路径字典序排序,一行搞定,非常简洁。
效果验证
这三种方法都能达到你想要的排序效果:比如192.168.83.173的条目会排在192.168.130.175前面(因为83<130),相同IP的条目会按路径的字母顺序排列(比如/index.html会在/products.html之前)。
备注:内容来源于stack exchange,提问作者龚健翔

