大家在Linux里处理文件时,肯定遇到过这样的场景:日志文件里的时间乱成一团、表格数据没有按规律排列、需要快速找到文件里的重复内容……这时候,sort命令就该登场了!它就像一个“文件内容排序大师”,能轻松搞定各种乱序数据,还能自定义排序规则,不管是新手还是老鸟,都是日常工作中离不开的高频命令。
今天这篇教程,就带大家从头到尾吃透sort命令——没有晦涩的专业术语堆砌,全是接地气的解释、可直接复制实操的示例,每个参数都拆解得明明白白,哪怕你是Linux新手,跟着练一遍也能彻底掌握。
一、先搞懂:sort命令到底能干啥?
sort命令的核心作用很简单:对文本文件的内容(或标准输入)按指定规则排序,然后输出排序结果。
举个最直观的例子:你有一个test.txt文件,里面的内容乱七八糟:
# 先创建一个测试文件(新手可直接复制执行)
echo -e "banana\napple\norange\ngrape" > test.txt
# 查看文件内容
cat test.txt
输出结果(乱序):
banana
apple
orange
grape
用sort命令一键排序:
sort test.txt
输出结果(按字母顺序排序):
apple
banana
grape
orange
这就是sort的基础用法——默认按“字符编码顺序”排序(英文按字母表,数字按字符顺序,后续会详细说)。除此之外,它还能实现:按数字大小排序、按指定字段排序、去重排序、合并已排序文件、检查文件是否有序等高级功能,咱们一步步来。
二、基础必学:sort核心参数(带实操示例)
sort的参数很多,但核心常用的就十几个,每个参数我都配了“作用说明+测试文件+命令+输出结果”,新手可以跟着复制命令实操,记得先创建测试文件,避免出错。
先准备一个通用测试文件test_all.txt(后续大部分示例都用这个文件,建议一次性创建好):
echo -e "10 apple\n2 banana\n5 grape\n3 orange\n10 cherry\n 8 pear\nabc 123\ndef 45" > test_all.txt
cat test_all.txt
test_all.txt文件内容(注意:有数字、字母、空格、重复数字):
10 apple
2 banana
5 grape
3 orange
10 cherry
8 pear
abc 123
def 45
2.1 按数字大小排序:-n(最常用!避开字符排序坑)
作用:默认情况下,sort会把数字当作“字符”排序(比如10会排在2前面,因为字符“1”比“2”小),-n参数就是告诉sort“按数字实际大小排序”,这是日常最常用的参数之一。
反面示例(字符排序,踩坑警告):
sort test_all.txt # 不加-n,按字符排序
输出结果(注意10排在2、3、5前面,因为字符“1”<“2”):
10 apple
10 cherry
2 banana
3 orange
5 grape
8 pear
abc 123
def 45
正确示例(按数字大小排序):
sort -n test_all.txt
输出结果(按数字从小到大排序,10在最后):
2 banana
3 orange
5 grape
8 pear
10 apple
10 cherry
abc 123
def 45
注意:文件中开头有空格的数字(比如“ 8 pear”),-n参数会自动忽略前面的空格,正常识别数字8,不用额外处理。
2.2 反向排序:-r(配合-n使用频率极高)
作用:将排序结果反向(默认从小到大,加-r后从大到小),通常和-n配合使用,实现“按数字降序排序”。
示例1:按数字降序排序:
sort -nr test_all.txt
输出结果(从大到小,10最先,2最后):
10 apple
10 cherry
8 pear
5 grape
3 orange
2 banana
abc 123
def 45
示例2:按字母反向排序(不用-n,直接反向字母顺序):
sort -r test.txt # 用之前的test.txt(纯字母)
输出结果(从z到a排序):
orange
grape
banana
apple
2.3 去重排序:-u(排序+去重一步到位)
作用:排序的同时,去除重复的行(只有完全相同的行才会被去重;如果配合-n,数字相同但后面内容不同,不会去重)。
补充:sort -u 等价于 sort | uniq,但sort -u效率更高,一步到位。
示例1:去除完全重复的行:
# 先创建一个有重复行的文件
echo -e "a\nb\na\nc\nb" > test_dup.txt
sort -u test_dup.txt # 排序+去重
输出结果(去重后按字母排序):
a
b
c
示例2:配合-n,数字相同但内容不同,不去重:
sort -nu test_all.txt # 按数字排序,去重(只有数字+内容完全相同才去重)
输出结果(10 apple和10 cherry数字相同但内容不同,保留两行):
2 banana
3 orange
5 grape
8 pear
10 apple
10 cherry
abc 123
def 45
踩坑提醒:如果想只按数字去重(不管后面内容),不能只用-u,需要配合-k参数(指定按第一个字段去重),后续会讲。
2.4 忽略大小写排序:-f
作用:排序时忽略字母的大小写(默认区分大小写,A和a会被当作不同字符),适合纯字母文件的排序。
示例:
# 创建一个大小写混合的文件
echo -e "Banana\napple\nOrange\ngrape" > test_case.txt
sort test_case.txt # 默认区分大小写(大写字母排在前面)
sort -f test_case.txt # 忽略大小写排序
输出结果对比:
# 区分大小写(默认)
Banana
Orange
apple
grape
# 忽略大小写(-f)
apple
Banana
grape
Orange
2.5 忽略开头空格:-b
作用:排序时忽略每行开头的空格(比如“ 8 pear”和“8 pear”,默认会被当作不同行,加-b后会当作同一类排序)。
示例:
# 创建一个有开头空格的文件
echo -e " apple\nbanana\n grape\norange" > test_space.txt
sort test_space.txt # 默认不忽略空格(空格行排在前面)
sort -b test_space.txt # 忽略开头空格排序
输出结果对比:
# 不忽略空格(默认)
apple
grape
banana
orange
# 忽略空格(-b)
apple
banana
grape
orange
实用场景:处理日志文件时,很多行开头会有空格或制表符,用-b可以避免空格导致的排序错乱。
2.6 按字典顺序排序:-d
作用:按“字典顺序”排序,只关注字母、数字和空格,忽略其他特殊字符(比如!、@、#等),适合处理带有特殊字符的文本。
示例:
# 创建一个有特殊字符的文件
echo -e "apple!\ngrape#\nbanana@\norange$" > test_dict.txt
sort test_dict.txt # 默认排序(特殊字符会影响顺序)
sort -d test_dict.txt # 按字典顺序排序(忽略特殊字符)
输出结果对比:
# 默认排序(特殊字符优先级高于字母)
apple!
banana@
grape#
orange$
# 字典排序(-d,只看字母,忽略特殊字符)
apple!
banana@
grape#
orange$
说明:这个示例中特殊字符不影响顺序,因为特殊字符位置相同;如果特殊字符在开头,比如“!apple”,默认会排在最前面,而-d会忽略“!”,按“apple”排序。
三、进阶必备:按字段/分隔符排序(-k、-t,核心难点拆解)
日常工作中,我们处理的文件大多是“多字段”的(比如表格、日志、CSV文件),比如“姓名 年龄 性别”“IP地址 时间 日志内容”,这时候就需要按“指定字段”排序——这是sort命令的核心难点,也是最实用的功能,咱们拆解得细一点。
3.1 指定字段分隔符:-t(必须先懂这个,否则-k用不对)
作用:告诉sort“字段之间用什么分隔”,默认情况下,sort会把“一个或多个空格/制表符”当作分隔符(比如“a b”和“a b”会被当作同一格式,两个字段)。
但实际场景中,我们可能用逗号(,)、冒号(:)、斜杠(/)等作为分隔符(比如/etc/passwd文件用冒号分隔),这时候就必须用-t指定分隔符。
示例1:默认分隔符(空格/制表符):
# test_all.txt的内容(空格分隔,两个字段:数字 水果)
cat test_all.txt
sort -n -k 1 test_all.txt # -k 1:按第一个字段(数字)排序,默认空格分隔
输出结果和之前sort -n一致(按第一个字段数字排序)。
示例2:指定冒号作为分隔符(实操/etc/passwd文件):
Linux系统中的/etc/passwd文件,每一行用冒号(:)分隔,格式为:用户名:密码占位符:UID:GID:描述:家目录:登录shell,比如:
# 查看/etc/passwd前5行
head -5 /etc/passwd
输出(示例):
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
需求:按UID(第三个字段)从小到大排序,查看前5行:
head -5 /etc/passwd | sort -t : -k 3 -n
命令解读:
-t : :指定分隔符为冒号(:),这样每一行就被分成了7个字段
-k 3 :按第三个字段(UID)排序
-n :按数字大小排序(UID是数字,必须加-n,否则按字符排序)
输出结果(按UID从小到大,和原顺序一致,因为原文件本身按UID排序):
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
3.2 指定排序字段:-k(重中之重,拆解用法)
作用:指定“按哪一个/哪一段字段”排序,格式为 -k POS1[,POS2],其中:
POS1:起始字段(必须写,比如-k 2就是从第二个字段开始)
POS2:结束字段(可选,默认到行尾;比如-k 2,2就是只按第二个字段排序,-k 2,3就是按第二个到第三个字段排序)
这里最容易踩坑:如果只写-k 2,不写POS2,sort会从第二个字段开始,一直排序到行尾,而不是只按第二个字段排序!咱们用示例对比,一看就懂。
先准备一个多字段测试文件test_field.txt:
echo -e "zhangsan 25 80\nlisi 22 90\nwangwu 25 75\nzhaoliu 22 85" > test_field.txt
cat test_field.txt
文件内容(空格分隔,3个字段:姓名 年龄 分数):
zhangsan 25 80
lisi 22 90
wangwu 25 75
zhaoliu 22 85
示例1:只按第二个字段(年龄)排序(正确写法:-k 2,2)
sort -t " " -k 2,2 -n test_field.txt
命令解读:-t " "(空格分隔),-k 2,2(只按第二个字段排序),-n(年龄是数字,按大小排序)
输出结果(按年龄从小到大,年龄相同的行,保持原顺序):
lisi 22 90
zhaoliu 22 85
zhangsan 25 80
wangwu 25 75
示例2:错误写法(只写-k 2,不写POS2)
sort -t " " -k 2 -n test_field.txt
命令解读:-k 2 表示“从第二个字段(年龄)开始,到行尾(年龄+分数)一起排序”,相当于按“年龄+分数”的组合排序
输出结果(年龄相同的行,再按分数从小到大排序):
zhaoliu 22 85
lisi 22 90
wangwu 25 75
zhangsan 25 80
踩坑总结:想“只按某一个字段”排序,必须写-k POS1,POS1(比如-k 2,2),否则会包含后面的字段一起排序!
示例3:按多个字段排序(主字段+次字段)
需求:先按年龄(第二个字段)从小到大排序,年龄相同的,再按分数(第三个字段)从大到小排序。
命令写法(多个-k参数,顺序就是优先级:先-k 2,2,再-k 3,3):
sort -t " " -k 2,2n -k 3,3nr test_field.txt
命令解读:
-k 2,2n :第一个排序字段(主字段):第二个字段,按数字排序(n)
-k 3,3nr :第二个排序字段(次字段):第三个字段,按数字(n)反向(r)排序
输出结果(年龄相同的,分数高的排在前面):
lisi 22 90
zhaoliu 22 85
zhangsan 25 80
wangwu 25 75
示例4:按字段的部分字符排序(高级用法)
有时候,我们不需要按整个字段排序,只需要按字段中的“某几个字符”排序,比如:按姓名的第二个字符排序、按IP地址的第三个段排序。
格式:-k F.C1,F.C2(F是字段号,C1是字段内的起始字符,C2是结束字符)
需求:按姓名(第一个字段)的第二个字符排序(test_field.txt文件):
sort -t " " -k 1.2,1.2 test_field.txt
命令解读:-k 1.2,1.2 表示“第一个字段(姓名)的第2个字符,只按这一个字符排序”
姓名拆解:zhangsan(第2个字符h)、lisi(i)、wangwu(a)、zhaoliu(h)
输出结果(按第2个字符的字母顺序排序:a < h < i):
wangwu 25 75
zhangsan 25 80
zhaoliu 22 85
lisi 22 90
四、实用场景参数(提升效率,日常必备)
这部分参数虽然用得不如-n、-r、-k频繁,但在特定场景下能极大提升效率,每个参数依然配实操示例。
4.1 输出到指定文件:-o(避免覆盖原文件踩坑)
作用:将排序后的结果输出到指定文件,而不是默认的屏幕(标准输出)。
踩坑提醒:不要用 sort test.txt > test.txt(重定向),这样会清空原文件!正确用法是用-o参数。
示例:
# 错误写法(清空原文件,慎点!)
sort test.txt > test.txt
# 正确写法(输出到sorted_test.txt)
sort test.txt -o sorted_test.txt
# 正确写法(排序后覆盖原文件,安全)
sort test.txt -o test.txt
4.2 合并已排序文件:-m(高效合并,不用重新排序)
作用:合并多个已经排好序的文件,输出一个新的排序文件——重点是“输入文件必须已经排序好”,否则合并结果会错乱,这样做比重新排序所有文件效率高很多。
示例:
# 先创建两个已经排序好的文件
echo -e "a\nb\nc" > sorted1.txt
echo -e "d\ne\nf" > sorted2.txt
# 合并两个已排序文件(不用重新排序,效率高)
sort -m sorted1.txt sorted2.txt -o merged.txt
# 查看合并结果
cat merged.txt
输出结果(合并后依然有序):
a
b
c
d
e
f
实用场景:日志按天拆分(每天的日志都已经按时间排序),想合并成一个完整的日志文件,用sort -m即可,不用重新排序所有日志,节省时间。
4.3 检查文件是否有序:-c、-C(快速校验)
有时候我们需要确认一个文件是否已经排好序,不用重新排序,用这两个参数即可快速校验。
-c(--check):检查文件是否有序,如果无序,打印出第一行乱序的内容,并退出状态为1;如果有序,无输出,退出状态为0。
-C(--check=quiet):和-c功能一致,但不打印乱序内容,只通过退出状态判断(0=有序,1=无序),适合在脚本中使用。
示例1:用-c检查乱序文件:
sort -c test.txt # test.txt是乱序的(之前创建的banana、apple、orange、grape)
输出结果(打印第一行乱序的内容):
sort: test.txt:2: disorder: apple
说明:第二行的apple比第一行的banana小,所以是乱序的。
示例2:用-C检查有序文件:
sort test.txt -o sorted_test.txt # 先创建一个有序文件
sort -C sorted_test.txt # 检查有序,无输出
echo $? # 查看退出状态(0=有序)
输出结果:0(表示文件已经有序)。
4.4 按月份排序:-M(处理日期类日志必备)
作用:按月份缩写(JAN、FEB、MAR……DEC)排序,忽略大小写,适合处理带有月份的日志文件。
示例:
# 创建一个带有月份的文件
echo -e "MAR 10\nJAN 5\nDEC 20\nFEB 15" > test_month.txt
sort -M test_month.txt # 按月份排序
输出结果(按1-12月顺序排序):
JAN 5
FEB 15
MAR 10
DEC 20
4.5 按人类可读格式排序:-h(处理大小单位必备)
作用:按“人类可读的大小单位”排序(比如K、M、G),比如100K、2M、1G,适合处理df、du、ls -lh命令的输出结果。
示例:
# 创建一个带有大小单位的文件
echo -e "100K\n2M\n500K\n1G\n50M" > test_size.txt
sort -h test_size.txt # 按人类可读格式排序
输出结果(按大小从小到大:K < M < G):
100K
500K
2M
50M
1G
实用场景:查看目录下文件大小,按大小排序:
ls -lh | sort -hr # -h按大小排序,-r反向(从大到小)
五、综合实战案例(结合实际工作,学以致用)
光会单个参数不够,实际工作中都是多个参数组合使用,这里给大家3个高频实战案例,跟着练一遍,就能应对80%的sort使用场景。
案例1:处理日志文件(按IP+时间排序)
需求:有一个nginx访问日志(access.log),每行格式为:IP地址 访问时间 访问路径,要求:先按IP地址从小到大排序,IP相同的,按访问时间(月份)排序。
先创建模拟日志文件access.log:
echo -e "192.168.1.100 [MAR/10/2024:10:00:00] /index.html
192.168.1.50 [JAN/05/2024:09:30:00] /login.html
192.168.1.100 [FEB/15/2024:14:20:00] /about.html
192.168.1.20 [DEC/20/2023:16:10:00] /contact.html
192.168.1.50 [FEB/08/2024:11:50:00] /home.html" > access.log
排序命令(拆解步骤):
# 按IP(第一个字段)从小到大,IP相同按月份(第二个字段的第3-5个字符)排序
sort -t " " -k 1,1V -k 2.3,2.5M access.log
命令解读:
-t " " :空格分隔字段
-k 1,1V :第一个字段(IP)按版本号排序(V参数,自动识别IP地址的分段,避免192.168.1.20排在192.168.1.100后面)
-k 2.3,2.5M :第二个字段(时间)的第3-5个字符(月份缩写),按月份排序(M参数)
输出结果(符合需求):
192.168.1.20 [DEC/20/2023:16:10:00] /contact.html
192.168.1.50 [JAN/05/2024:09:30:00] /login.html
192.168.1.50 [FEB/08/2024:11:50:00] /home.html
192.168.1.100 [FEB/15/2024:14:20:00] /about.html
192.168.1.100 [MAR/10/2024:10:00:00] /index.html
案例2:处理CSV文件(按多字段排序+去重)
需求:有一个学生成绩CSV文件(score.csv),格式为:姓名,班级,分数,要求:按班级(第二个字段)从小到大排序,班级相同的按分数(第三个字段)从大到小排序,去除班级和分数完全相同的重复行。
创建模拟CSV文件score.csv:
echo -e "张三,1班,90
李四,2班,85
王五,1班,90
赵六,2班,88
孙七,1班,80
周八,2班,85" > score.csv
排序命令:
sort -t "," -k 2,2 -k 3,3nr -u score.csv -o sorted_score.csv
命令解读:
-t "," :CSV文件用逗号分隔
-k 2,2 :按第二个字段(班级)排序
-k 3,3nr :按第三个字段(分数)数字反向排序(从大到小)
-u :去除班级和分数完全相同的重复行(王五和张三班级、分数都相同,去重后保留一行)
-o sorted_score.csv :输出到新文件
输出结果(sorted_score.csv):
张三,1班,90
孙七,1班,80
赵六,2班,88
李四,2班,85
案例3:按文件长度排序(实用技巧)
需求:查看当前目录下所有文件的名称和大小(按字节数),并按文件大小从小到大排序。
命令(结合awk、sort、cut,实战常用组合):
# 拆解:awk打印文件长度和名称,sort按长度排序,cut去掉长度字段,只保留文件名
ls -l | awk '{print $5, $9}' | sort -n | cut -d " " -f 2-
命令解读:
ls -l :查看文件详细信息(\$5是文件大小,\$9是文件名)
awk '{print \$5, \$9}' :提取文件大小(第5字段)和文件名(第9字段),用空格分隔
sort -n :按文件大小(数字)从小到大排序
cut -d " " -f 2- :去除第一个字段(文件大小),只保留文件名
输出结果(示例,按当前目录文件大小排序):
test.txt
test_all.txt
access.log
sorted_score.csv
最后提醒:最好的学习方式是“实操”,把教程中的示例一个个复制执行,修改参数看看效果,多试几次,就能彻底掌握sort命令——它看似简单,但用好它,能节省大量处理文本的时间,是Linux运维、开发必备的高频命令之一。