处理日志、导出数据、统计访问量时,sort、uniq、wc、cut、tr 这五条命令是管道里最常出现的组合。它们单个都不复杂,拼在一起却能干掉一大半"把一大坨文本整理成人能看的东西"的活。这一篇把语法、参数和实战用法过一遍。
一、sort:排序
语法格式
sort [选项] [文件]
不带文件时从标准输入读。默认按字典序排,这也是新手最容易踩的坑:10 会排在 2 前面。
常用参数
| 参数 | 说明 |
|---|---|
-n |
按数值大小排序,不是字典序 |
-r |
逆序(从大到小 / Z 到 A) |
-k 字段号 |
按第几个字段排序,字段从 1 开始 |
-t 分隔符 |
指定字段分隔符,默认是空白 |
-u |
排序后去重(等价于 sort 后接 uniq) |
-f |
忽略大小写 |
-h |
按人类可读数字排序(1K、2M、3G) |
-M |
按月份缩写排序(JAN、FEB...) |
示例与预期输出
先准备一份测试数据 visits.log,每行是"IP 访问次数":
192.168.1.10 320
10.0.0.5 15
192.168.1.10 320
172.16.0.8 89
10.0.0.5 15
192.168.1.200 1024
示例 1:按第二列数值倒序
sort -k2 -nr visits.log
预期输出:
192.168.1.200 1024
192.168.1.10 320
172.16.0.8 89
10.0.0.5 15
192.168.1.10 320
10.0.0.5 15
-k2 指定按第二列,-n 告诉 sort 这列是数字,-r 倒过来。注意重复行还在,去重交给后面的 uniq。
示例 2:按冒号分隔的字段排序
/etc/passwd 用冒号分 7 段,想按 UID(第三段)排:
sort -t: -k3 -n /etc/passwd | head -5
预期输出:
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
-t: 把分隔符换成冒号,-k3 才不会按整行乱排。
二、uniq:去重
语法格式
uniq [选项] [输入文件 [输出文件]]
常用参数
| 参数 | 说明 |
|---|---|
-c |
在每行前打印出现次数 |
-d |
只显示重复过的行 |
-u |
只显示从未重复的行 |
-i |
忽略大小写 |
⚠️ 关键前提:先排序再 uniq
uniq 只合并相邻 的重复行。原始数据里 192.168.1.10 出现两次但中间隔着别的行,直接 uniq visits.log 是去不掉的。所以标准套路是:
sort visits.log | uniq -c
预期输出:
2 10.0.0.5 15
1 172.16.0.8 89
2 192.168.1.10 320
1 192.168.1.200 1024
-c 把每行出现次数放在最前面。想找"谁访问次数最多",再接一层排序:
sort visits.log | uniq -c | sort -k1 -nr
预期输出:
2 192.168.1.10 320
2 10.0.0.5 15
1 192.168.1.200 1024
1 172.16.0.8 89
这就是日志统计里最经典的一条管道:排序、去重计数、再按次数倒序。
三、wc:统计行数、单词数、字节数
语法格式
wc [选项] [文件]
常用参数
| 参数 | 说明 |
|---|---|
-l |
只统计行数(最常用) |
-w |
统计单词数 |
-c |
统计字节数 |
-m |
统计字符数(中文场景下比 -c 准) |
示例与预期输出
wc -l /etc/passwd
预期输出:
35 /etc/passwd
不带参数时三个数一起出:
wc /etc/passwd
预期输出(字段依次是行、单词、字节):
35 110 1680 /etc/passwd
配合管道用得最多:ps aux | wc -l 数进程,find . -name "*.py" | wc -l 数文件个数。注意 find ... | wc -l 比 ls | wc -l 靠谱,后者遇到文件名带换行符会数错。
四、cut:按列切文本
语法格式
cut [选项] 文件
常用参数
| 参数 | 说明 |
|---|---|
-d 分隔符 |
指定字段分隔符 |
-f 列号 |
取第几列,-f1,3 取 1 和 3,-f2- 取第 2 列到末尾 |
-c 字符位置 |
按字符位置切,-c1-10 取前 10 个字符 |
示例与预期输出
把 /etc/passwd 的用户名(第 1 列)和登录 Shell(第 7 列)抽出来:
cut -d: -f1,7 /etc/passwd | head -5
预期输出:
root:/bin/bash
daemon:/usr/sbin/nologin
bin:/usr/sbin/nologin
sys:/usr/sbin/nologin
sync:/bin/sync
-d: 和 -f1,7 是固定搭配。想只看哪些账号能真正登录:
grep -v nologin /etc/passwd | cut -d: -f1,7
五、tr:字符替换与删除
tr 和前面几个不一样,它不接文件名,只读标准输入,主要靠管道喂数据。
语法格式
echo "文本" | tr [选项] 集合1 [集合2]
常用参数
| 参数 | 说明 |
|---|---|
-d 集合 |
删除集合中出现的字符 |
-s 集合 |
把连续重复的字符压缩成一个 |
| 无参数 | 把集合 1 里的每个字符替换成集合 2 对应位置的字符 |
示例与预期输出
小写转大写:
echo "Hello World" | tr 'a-z' 'A-Z'
预期输出:
HELLO WORLD
去掉文本里的换行和多余空格:
echo "a b c" | tr -s ' '
预期输出:
a b c
把 Windows 换行(\r\n)里的 \r 删掉,变相实现 dos2unix:
tr -d '\r' < windows.txt > unix.txt
\r 在 tr 里写成 '\r'。这条命令处理从 Windows 拷过来的脚本文件特别好使。
六、知识扩展:sort -u 和 uniq 的取舍,以及字段排序的边界
sort -u 和 sort | uniq 都能去重,区别在哪?
sort -u 只保留每组重复里第一行 ;uniq 不加 -c 时行为一样。但当你需要"既去重又要看到每组出现几次",必须 sort | uniq -c,sort -u 给不出计数。性能上两者差别可以忽略,写法按目的选。
字段排序还有个容易翻车的点:-k2 不是"从第 2 个字符开始",而是"从第 2 个字段的开头,一直排到行尾"。想精确到字段内部的某个字符位置,得写 -k2.1,2.3 这种"起始.结束"格式。比如想按 IP 第三段排,直接 -k3 -t. 会按字典序把 10 排在 8 前面,这时候要加 -n 才对。
最后一个组合技巧:统计 Nginx 日志里访问量最高的 5 个 IP。
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -k1 -nr | head -5
整条链路读下来就是:awk 抽第一列 IP → sort 排序让相同 IP 相邻 → uniq -c 计数 → sort -k1 -nr 按次数倒序 → head 取前 5。awk 那一列后面会专门讲,这里先把管道串起来感受一下。