![在 shell 中使用多个 -t 进行排序](https://linux22.com/image/1032258/%E5%9C%A8%20shell%20%E4%B8%AD%E4%BD%BF%E7%94%A8%E5%A4%9A%E4%B8%AA%20-t%20%E8%BF%9B%E8%A1%8C%E6%8E%92%E5%BA%8F.png)
我有一个包含以下内容的文件:
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
将键拆分为进一步的子字段,其中是实际字段编号,是字段内的字符位置。因此KEYDEF
F.C
F
C
在这个特殊情况下前两个八位字节都是 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,4
IP 地址排序似乎不正确。如果您只想使用.
作为分隔符进行排序,则需要使用-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
选项比一堆更干净-k
。LC_ALL=C
也很好。有关更清晰的解决方案,请参阅@steeldriver 的回答。