跳转至

sort、uniq 与计数去重

sort 对行进行排序,uniq 只比较相邻行。要对分散在文件各处的重复值计数,通常先排序再使用 uniq

sort 基础

sort names.txt
sort -r names.txt
sort -n numbers.txt
sort -nr numbers.txt
sort -u names.txt
选项 作用
-n 数值排序
-r 逆序
-u 排序并去重
-f 忽略大小写
-t 指定字段分隔符
-k 指定排序键
-h 按 K/M/G 等人类可读单位排序,非 POSIX
-V 版本号自然排序,GNU/BSD 支持情况需确认

按字段排序

sort -t: -k3,3n /etc/passwd
sort -k2,2nr usage.txt
sort -k1,1 -k3,3n file

-k2,2n 表示只按第 2 字段数值排序;写成 -k2n 可能把第 2 字段到行尾都当作键,结果与预期不同。

uniq

uniq file
uniq -c file
uniq -d file
uniq -u file
选项 作用
-c 在每行前显示相邻重复次数
-d 只显示重复值
-u 只显示只出现一次的值
-i 比较时忽略大小写

错误示例:

uniq access.log

如果相同内容不相邻,它不会被去重。正确组合:

sort access.log | uniq
sort access.log | uniq -c | sort -nr

Top N

# 出现次数最多的客户端 IP
awk '{print $1}' access.log \
  | sort \
  | uniq -c \
  | sort -nr \
  | head -20

理解每一阶段:

awk          只留下 IP
sort         把相同 IP 排在一起
uniq -c      对相邻相同 IP 计数
sort -nr     按计数从大到小
head -20     取前 20 项

Locale 的影响

字符排序受 LC_COLLATE 影响。同一脚本在不同语言环境可能得到不同顺序;处理机器数据和稳定结果时可指定:

LC_ALL=C sort file

C locale 通常更快且按字节顺序,但中文等自然语言排序不符合人类习惯。不要把改变 locale 当成无条件优化。

大文件

sort 会使用内存和临时文件。大日志排序前先按时间/字段过滤,确认临时目录空间:

sort -S 1G -T /data/tmp large.txt

-S-T 的细节可能因实现不同。根分区较小时,默认临时目录也可能被大排序撑满。

集合操作

sort -u file1 file2                  # 并集
sort file1 file2 | uniq -d           # 交集
sort file1 file1 file2 | uniq -u     # file2 相对 file1 的差异式用法需理解数据集合

更清晰的两个已排序文件比较可使用 comm

LC_ALL=C sort -u file1 > file1.sorted
LC_ALL=C sort -u file2 > file2.sorted
comm -12 file1.sorted file2.sorted   # 交集
comm -23 file1.sorted file2.sorted   # 只在 file1

官方参考:GNU Coreutils sort/uniq