Linux sort 命令

本文深入解析Shell脚本中sort命令的高级应用,涵盖字段定义、sortkey指定及修饰符使用,通过实例展示如何精确控制文本排序,适用于需要掌握复杂数据处理技能的系统管理员和程序员。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

默认排序方式

现有如下文本

$ cat hard-sort.txt 
 3
1234
6
Adb
85
Abc
aAb
aab

现在我想用 sort 命令对这些行进行排序

$ sort hard-sort.txt 
 3
1234
6
85
Abc
Adb
aAb
aab

这个排序结果似乎是杂乱无章的,但实际是有规律可循的。sort 命令把文件的每一行当做一个字符串,然后以比较字符串的方式来对行进行排序。

字符串比较方式是逐个比较字符。

字段的定义

在实际中,通常是根据行中的某个列进行排序的,这个列在 shell 中被称为字段。

假如即将被排序的某一行如下

-rw-r--r--  1 david  staff   29 12 28 22:04 hard-sort.txt

字段默认是被空白字符(空格和tab)分割的,因此这一行其实可以被分割为9个字段。然而字段的值却包括字段开头的空白字符,例如第五个字段的值其实是三个空格再加上29。

然而如果在使用 sort 命令的时候,加上 -b 选项,那么字段就不包括开头的空白字符。

-k 选项

sort 命令的 -k 选项用来指定排序用到的 sort key,语法如下

sort -k field1[,field2]

field1 和 field2 的格式为 m.n 。m 表示行中字段的序号,从 1 开始。n 表示第 m 个字段的第 n 个字符,并且 n 也是从 1 开始。

因此 field1 和 field2 指定了一个区间的字符串,而这个字符串就是用来排序的 sort key,它有如下规则

  1. 如果 field2 被省略,那么 sort key 字符串结束于行尾。
  2. m.n 后面可以加上修饰符,例如 b, n, r 。这些修饰符对应于 sort 相应的选项,例如 -b 选项表示忽略字段开头的空白字符,-n 选项表示把 sort key 看作数值而不是字符串,-r 选项表示反向排序。
  3. 除了修饰符 b 以外,其它的修饰符,例如 n ,只需要使用一次,即可对 field1 和 field2 同时起作用。
  4. 如果省略了 field1 中的 .n ,那么默认为 .1,表示 sort key 起始于 field1 指定的第 m 个字段中的第一个字符。
  5. 如果没有指定 field2 中的 .n ,那么 sort key 字符串结束于 field2 指定的第 m 个字段的末尾。

例子分析

有了这些理论基础并不代表你在实际中能够使用好 sort 命令,我们通过一个例子来加深理解。

假设现在有一个输出,如下

$ ls -l
total 64
-rwxr--r--  1 david  staff  654 12 24 22:51 file_expression.sh
-rwxr--r--  1 david  staff  330 12 24 23:07 integer_expression.sh
-rwxr--r--  1 david  staff  513 12 24 23:23 integer_expression2.sh
-rw-r--r--  1 david  staff   40 12 28 22:33 person.txt
-rw-r--r--  1 david  staff    5 12 28 23:33 short-size.txt
-rwxr--r--  1 david  staff  469 12 24 23:03 string_expression.sh
-rwxr--r--  1 david  staff  162 12 24 22:56 test.sh
-rwxr--r--  1 david  staff  387 12 24 23:36 test_operator.sh

现在我需要对这个结果根据文件的大小进行排序,那么就需要使用 sort 命令根据第五个字段进行排序

$ ls -l | sort -k 5,5
total 64
-rw-r--r--  1 david  staff    5 12 28 23:33 short-size.txt
-rw-r--r--  1 david  staff   40 12 28 22:33 person.txt
-rwxr--r--  1 david  staff  162 12 24 22:56 test.sh
-rwxr--r--  1 david  staff  330 12 24 23:07 integer_expression.sh
-rwxr--r--  1 david  staff  387 12 24 23:36 test_operator.sh
-rwxr--r--  1 david  staff  469 12 24 23:03 string_expression.sh
-rwxr--r--  1 david  staff  513 12 24 23:23 integer_expression2.sh
-rwxr--r--  1 david  staff  654 12 24 22:51 file_expression.sh

-k 5,5 表示 sort key 的值开始于第五个字段(包括空白字符)第一个字符,结束于第五个字段的最后一个字符,也就是说 sort key 就是第五个字段(包括空白字符)。而 -k 5 表示 sort key 的值为起始于第五个字段开头,结束于行尾。

这个结果看起来是正确,其实是一个巧合而已。现在我们去掉字段开头的空白,来看看排序的结果

$ ls -l | sort -k 5b,5b 
total 64
-rwxr--r--  1 david  staff  162 12 24 22:56 test.sh
-rwxr--r--  1 david  staff  330 12 24 23:07 integer_expression.sh
-rwxr--r--  1 david  staff  387 12 24 23:36 test_operator.sh
-rw-r--r--  1 david  staff   40 12 28 22:33 person.txt
-rwxr--r--  1 david  staff  469 12 24 23:03 string_expression.sh
-rw-r--r--  1 david  staff    5 12 28 23:33 short-size.txt
-rwxr--r--  1 david  staff  513 12 24 23:23 integer_expression2.sh
-rwxr--r--  1 david  staff  654 12 24 22:51 file_expression.sh

修饰符 b 去掉了字段开头的空白字符,我们发现排序的结果已经混乱了,讲道理不应该呀,我只是去掉了空白字符而已,这样的结果是为什么呢?

只有修饰符 b 才分别需要对 field1 和 field2 使用,这样才能同时去掉 field1 和 field2 指定字段前的空白符。然而其它的修饰符只需要使用一次,即可对 field1 和 field2 同时起作用。

第五个字段去掉了开头的空白字符,然而第五个字段还是一个字符串,它不是整型,因此 sort 命令还是通过字符串的比较方式进行排序的,那么出面上面的结果就不意外了。

那么我们怎么纠正上面的结果呢?使用 -n 选项,按照字符串的数字值进行排序。

$ ls -l | sort -k 5b,5bn
total 64
-rw-r--r--  1 david  staff    5 12 28 23:33 short-size.txt
-rw-r--r--  1 david  staff   40 12 28 22:33 person.txt
-rwxr--r--  1 david  staff  162 12 24 22:56 test.sh
-rwxr--r--  1 david  staff  330 12 24 23:07 integer_expression.sh
-rwxr--r--  1 david  staff  387 12 24 23:36 test_operator.sh
-rwxr--r--  1 david  staff  469 12 24 23:03 string_expression.sh
-rwxr--r--  1 david  staff  513 12 24 23:23 integer_expression2.sh
-rwxr--r--  1 david  staff  654 12 24 22:51 file_expression.sh

修饰符 n 表示把 sort key 当作数值来对行进行排序。现在这个结果就不是巧合了,这是正确使用选项而导致的结果。

现在我不想用文件大小来对行进行排序,而是使用第八个字段,也就是小时和分钟组合的时间。但是很不幸,sort 没有这个选项来比较这样格式的时间,因此我需要分别比较小时,然后比较分钟。

首先使用小时来排序

$ ls -l | sort -k 8.1b,8.2bn
total 64
-rw-r--r--  1 david  staff   40 12 28 22:33 person.txt
-rwxr--r--  1 david  staff  162 12 24 22:56 test.sh
-rwxr--r--  1 david  staff  654 12 24 22:51 file_expression.sh
-rw-r--r--  1 david  staff    5 12 28 23:33 short-size.txt
-rwxr--r--  1 david  staff  330 12 24 23:07 integer_expression.sh
-rwxr--r--  1 david  staff  387 12 24 23:36 test_operator.sh
-rwxr--r--  1 david  staff  469 12 24 23:03 string_expression.sh
-rwxr--r--  1 david  staff  513 12 24 23:23 integer_expression2.sh

8.1b,8.2bn 的意思是,第八个字段去掉了前面的空白字符,然后使用它的第一个字符和第二个字符作为 sort key , 并根据 sort key 的数字值对行进行排序。

现在已经按小时排好序,如果小时相等,那再按分钟进行排序

$ ls -l | sort -bn -k 8.1,8.2 -k 8.4,8.5  
total 64
-rw-r--r--  1 david  staff   40 12 28 22:33 person.txt
-rwxr--r--  1 david  staff  654 12 24 22:51 file_expression.sh
-rwxr--r--  1 david  staff  162 12 24 22:56 test.sh
-rwxr--r--  1 david  staff  469 12 24 23:03 string_expression.sh
-rwxr--r--  1 david  staff  330 12 24 23:07 integer_expression.sh
-rwxr--r--  1 david  staff  513 12 24 23:23 integer_expression2.sh
-rw-r--r--  1 david  staff    5 12 28 23:33 short-size.txt
-rwxr--r--  1 david  staff  387 12 24 23:36 test_operator.sh

这里首先利用 -k 8.1,8.2 按小时对行进行排序,然后使用 -k 8.4, 8.5 按分钟对行进行排序,并且这两个排序都受到 -bn 影响,也就是去掉字段开头的空白字符以及把 sort key 当作数字值而不是字符串。

-bn -k 8.1,8.2 -k 8.4,8.5 与 -k 8.1b,8.2bn -k 8.4b,8.5bn 的效果是一样的,区别在于,如果 n, b 字符用于选项,那么全局有效,而如果只是用作修饰符,只局部有效。

-t 选项

sort 命令默认是使用空白字符(空格和tab)来对行中的字段进行分割,然而在实际中,文本字段的分割并不总是使用空白字符,例如 /etc/passwd 文本就是使用冒号进行分割字段的。如果遇到这样的不使用空白字符分割字段的文本,就用 -t 选项重新定义分割字符。

假如现在有如下文本

$ cat person.txt 
David;22
Pekky;11
Panda;18

这个文本的行的字段是用分号进行分割的,如果想用 sort 命令进行排序,就必须使用 -t 选项重新定义字段分割符为冒号

$ sort -t ';' -k 2n person.txt
Pekky;11
Panda;18
David;22

-t ‘;’ 重新定义了 sort 命令使用的字段分割符为分号,-k 2n 表示使用第二个字段,按照数字值进行排序。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值