在 shell 中使用多个 -t 进行排序

在 shell 中使用多个 -t 进行排序

我有一个包含以下内容的文件:

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 数量排序,如果相同则使用第四个以制表符分隔的字母文件名access-2014-09-04.log。我试过sort -t. -k1,2 -k3,4 -n -t $'\t' -k4 access-2014-09-04.log

但它告诉我sort: incompatible tabs

答案1

A数字( -n) 对 IP 地址进行排序只会将前两个八位字节视为“数字” - 您可以使用以下--debug选项进行验证:

$ LC_ALL=C sort --debug --stable -t $'\t' -k1,1n access-2014-09-04.log 
sort: text ordering performed using simple byte comparison
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
_______

--stable为了清楚起见,我添加省略了最后的整行词汇排序)。

据我所知,您不能在单个命令中使用两个不同的字段分隔符- 尽管您可以使用以下形式sort将键拆分为进一步的子字段,其中是实际字段编号,是字段内的字符位置。因此KEYDEFF.CFC在这个特殊情况下前两个八位字节都是 7 个字符长,你可以伪造一个“点分隔”数字排序的第一个制表符分隔的 true 字段(其实不要这样做——它只是为了说明符号F.C,在现实生活中会失败,因为八位字节可能有 1、2 或 3 个字符长) 使用-k1.1,1.7n -k1.9,1n

$ LC_COLLATE=C sort --debug -t $'\t' -k1.1,1.7n -k1.9,1n -s access-2014-09-04.log 
sort: text ordering performed using simple byte comparison
192.168.83.173>2014-09-04>10:05:17>/products.html
_______
        ______
192.168.83.173>2014-09-04>12:19:55>/products/560.html
_______
        ______
192.168.130.175>2014-09-04>10:25:01>/index.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.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
_______
        _______

我怀疑你真正想要的是自然地按 IP 排序版本-V)然后按照默认词汇顺序按名称排序:

$ sort -t $'\t' -k1,1V -k4 access-2014-09-04.log 
192.168.83.173  2014-09-04      12:19:55        /products/560.html
192.168.83.173  2014-09-04      10:05:17        /products.html
192.168.130.175 2014-09-04      10:25:01        /index.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.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

指定稳定排序 ( -s/ --stable) 似乎没有必要,因为其余字段(日期和时间)似乎无论如何都是按词汇顺序排列的。请注意,将按您的语言环境的词汇顺序对文件名列进行排序 - 添加LC_ALL=C以强制严格字节顺序。

答案2

使用装饰-排序-去装饰习语使用任意 awk+sort+cut:

$ awk -v OFS='\t' '{ip=$1; gsub(/\./,OFS,ip); print ip, $0}' file |
    sort -k1,1n -k2,2n -k3,3n -k4,4n -k8 | cut -f5-
192.168.83.173  2014-09-04      10:05:17        /products.html
192.168.83.173  2014-09-04      12:19:55        /products/560.html
192.168.130.175 2014-09-04      10:25:01        /index.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.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

答案3

您可以使用两个 s 将命令拆分为管道sort

sort -t $'\t' -k4 access-2014-09-04.log | sort -t. -k1,2 -k3,4 -n -s

输出:

192.168.83.173  2014-09-04      12:19:55        /products/560.html
192.168.83.173  2014-09-04      10:05:17        /products.html
192.168.130.175 2014-09-04      10:25:01        /index.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.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

首先,我按照第二个标准(路径)进行排序。现在列表按路径顺序排序。然后我按照第一个标准进行排序。我添加了-s一个稳定排序,以便不会浪费第二个排序标准。结果是,如果第一个标准不同,则将考虑所述第一个标准,但如果相同,则将保持第一步中的先前顺序(按第一个标准排序)。

顺便说一句,您的-k1,2 -k3,4IP 地址排序似乎不正确。如果您只想使用.作为分隔符进行排序,则需要使用-k1,1 -k2,2 -k3,3 -k4,4

sort -t $'\t' -k4 access-2014-09-04.log | sort -t. -k1,1 -k2,2 -k3,3 -k4,4 -n -s

选项--debug向我揭示了这一点。但是我认为-V选项比一堆更干净-kLC_ALL=C也很好。有关更清晰的解决方案,请参阅@steeldriver 的回答

相关内容