uniq 命令详解与参数实测(Ubuntu 22.04)
uniq 命令逐参数实测:相邻去重原理、-c/-d/-u/-i 计数筛选、-w/-s/-f 比较范围收窄与先 sort 惯用法,全部回显在 Ubuntu 22.04(GNU coreutils 8.32)实测通过。
uniq 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 GNU uniq 的相邻去重机制与每一个常用参数——
-c计数、-d/-u重复与唯一筛选、-i忽略大小写、-w/-s/-f三种比较范围收窄,以及「必须先 sort 才能去重」的相邻性原理,全部命令与回显已在 Ubuntu 22.04(GNU coreutils 8.32)实测通过。
一、环境信息
本节记录实测环境与测试数据,便于读者按相同条件复现本文全部命令与回显。
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- uniq 版本:GNU coreutils 8.32(
uniq --version实测) - 默认 locale:
LANG未设置(等效 C locale,大小写按字节值比较) - 实测工作目录:
/root/demo/uniq/ - 测试数据:
visitors.txt(含重复与非相邻访客名单)、ips.txt(IP 与请求方式)、tasks.log(带级别前缀的任务日志)、access.txt(带日期的访问日志)
二、命令概览
2.1 语法与测试数据准备
uniq 的基本语法为 uniq [OPTION]... [INPUT [OUTPUT]]:过滤 INPUT(或标准输入)中相邻的重复行,写入 OUTPUT(或标准输出);无参数时把相邻重复行合并为其第一次出现。测试前先准备一套演示文件,后续所有参数实测都基于这套数据:
mkdir -p /root/demo/uniq
cat > /root/demo/uniq/visitors.txt <<'EOF'
alice
bob
bob
alice
bOb
carol
dave
EOF
cat > /root/demo/uniq/ips.txt <<'EOF'
192.168.1.101 GET
192.168.1.102 GET
192.168.1.103 POST
192.168.2.201 GET
EOF
cat > /root/demo/uniq/tasks.log <<'EOF'
[INFO] task done
[WARN] task done
[INFO] task failed
EOF
cat > /root/demo/uniq/access.txt <<'EOF'
2026-10-15 GET /api
2026-10-16 GET /api
2026-10-16 POST /api
2026-10-17 GET /api
EOF
验证数据就绪(visitors.txt 共 7 行:alice 2 次、bob 2 次、bOb 1 次、carol 与 dave 各 1 次,且重复行分散在文件不同位置):
$ ls -l /root/demo/uniq
total 16
-rw-r--r-- 1 root root 81 Oct 15 07:46 access.txt
-rw-r--r-- 1 root root 73 Oct 15 07:46 ips.txt
-rw-r--r-- 1 root root 53 Oct 15 07:46 tasks.log
-rw-r--r-- 1 root root 35 Oct 15 07:46 visitors.txt
$ cat /root/demo/uniq/visitors.txt
alice
bob
bob
alice
bOb
carol
dave
💡 说明:
bOb是刻意安排的大小写混合行——C locale 排序后它恰好与两个bob相邻,3.6 节将用它实测-i的合并效果。
2.2 --help 全量回显
uniq --help 输出全部参数清单(实测原样保留,可作为参数速查):
Usage: uniq [OPTION]... [INPUT [OUTPUT]]
Filter adjacent matching lines from INPUT (or standard input),
writing to OUTPUT (or standard output).
With no options, matching lines are merged to the first occurrence.
Mandatory arguments to long options are mandatory for short options too.
-c, --count prefix lines by the number of occurrences
-d, --repeated only print duplicate lines, one for each group
-D print all duplicate lines
--all-repeated[=METHOD] like -D, but allow separating groups
with an empty line;
METHOD={none(default),prepend,separate}
-f, --skip-fields=N avoid comparing the first N fields
--group[=METHOD] show all items, separating groups with an empty line;
METHOD={separate(default),prepend,append,both}
-i, --ignore-case ignore differences in case when comparing
-s, --skip-chars=N avoid comparing the first N characters
-u, --unique only print unique lines
-z, --zero-terminated line delimiter is NUL, not newline
-w, --check-chars=N compare no more than N characters in lines
--help display this help and exit
--version output version information and exit
A field is a run of blanks (usually spaces and/or TABs), then non-blank
characters. Fields are skipped before chars.
Note: 'uniq' does not detect repeated lines unless they are adjacent.
You may want to sort the input first, or use 'sort -u' without 'uniq'.
GNU coreutils online help: <https://www.gnu.org/software/coreutils/>
Report any translation bugs to <https://translationproject.org/team/>
Full documentation <https://www.gnu.org/software/coreutils/uniq>
or available locally via: info '(coreutils) uniq invocation'
💡 说明:帮助末尾的 Note 是 uniq 的灵魂——它只检测相邻重复行,通常应先 sort 再 uniq(或直接用
sort -u)。另外「Fields are skipped before chars」表明-f(跳字段)先于-s(跳字符)生效,两者可叠加,见 3.9 节说明。
2.3 版本确认
$ uniq --version | head -1
uniq (GNU coreutils) 8.32
三、参数逐项实测
本章逐项实测每个参数:每节先给命令与真实回显,再解释行为要点。
3.1 默认行为:只合并相邻重复行
$ uniq /root/demo/uniq/visitors.txt
alice
bob
alice
bOb
carol
dave
7 行输入输出 6 行:第 2、3 行的两个相邻 bob 被合并,而第 1、4 行的两个 alice 因中间隔着 bob 不相邻,原样保留两个。这就是 uniq 的基本模型——按行扫描,当前行与上一行相同就丢弃当前行。
3.2 未先 sort:uniq 失效(相邻性原理)
文件里有几个 alice?用 grep 证明它出现了 2 次,但 uniq 没有合并它们:
$ grep -c alice /root/demo/uniq/visitors.txt
2
$ sort /root/demo/uniq/visitors.txt | uniq
alice
bOb
bob
carol
dave
第一条证明 alice 出现 2 次;第二条先 sort 再 uniq,7 行去重成 5 行,alice 与 bOb、bob 各归其位(C locale 排序把 bOb 排在 bob 前,大小写不同的行互不去重)。结论:uniq 只认相邻重复,全局去重必须先 sort——这也是 sort visitors.txt | uniq 成为固定搭配的原因。
💡 技术拓展:文本去重有三个常见档位,选型取决于「要不要保持出现顺序」:
方式 是否需排序 顺序保持 适用场景 uniq不需要(仅相邻去重) 保持 已有序数据(如日志按时间)的相邻去重 sort -u/ `sortuniq` 需要 打散 awk '!seen[$0]++'不需要 首次出现顺序 保持原始顺序的全局去重 本文主角 uniq 胜在零依赖、可与计数/筛选参数组合;日志类已按时间有序的数据直接 uniq 即可,无序数据要保序则用 awk 哈希去重。
3.3 -c:在行首附加出现次数
$ sort /root/demo/uniq/visitors.txt | uniq -c
2 alice
1 bOb
2 bob
1 carol
1 dave
每组相邻重复的第一行前加上出现次数(默认右对齐 7 列宽度)。sort | uniq -c 是统计词频、IP 频次类需求的基石,再接一个 sort -rn 就是频次 TopN(见四、实战示例)。
3.4 -d:只显示重复行(每组一行)
$ sort /root/demo/uniq/visitors.txt | uniq -d
alice
bob
只输出「出现次数大于 1 的组」,每组打印一次(打印的是该组第一行原文)。bOb 只出现 1 次,不算重复。找「名单里哪些项出现了不止一次」就用 -d。
3.5 -u:只显示唯一行
与 -d 相反,-u 只输出「恰好出现 1 次的组」:
$ sort /root/demo/uniq/visitors.txt | uniq -u
bOb
carol
dave
alice 与 bob 各出现 2 次被排除,剩 3 行。-d 与 -u 合起来把输入按「重复 / 唯一」一分为二。
3.6 -i:忽略大小写比较
先看不加 -i 的计数(3.3 节),再加 -i 对照:
$ sort /root/demo/uniq/visitors.txt | uniq -i -c
2 alice
3 bOb
1 carol
1 dave
$ sort /root/demo/uniq/visitors.txt | uniq -i -d
alice
bOb
加上 -i 后 bOb 与两个 bob 因忽略大小写被视为同一组,计数从 1 与 2 变成 3,且组内第一行 bOb 被打印;-i -d 也把 alice 与 bOb 都列为重复。注意各组打印的仍是组内第一行的原文(bOb 不会被改写成 bob)。
3.7 -w N:每行只比较前 N 个字符
-w 10 限制只比较每行前 10 个字符。ips.txt 前 3 行的前 10 个字符都是 192.168.1.,第 4 行是 192.168.2.:
$ uniq -w 10 -c /root/demo/uniq/ips.txt
3 192.168.1.101 GET
1 192.168.2.201 GET
前 3 行被并入同一组计数 3,输出打印组内第一行全文。这个用法常用于按「前缀」分组统计——IP 前缀、日志时间戳前缀、文件路径前缀等。
3.8 -s N:跳过每行前 N 个字符再比较
-s 7 跳过 [INFO] 这 7 个字符(含空格),从第 8 个字符开始比较:
$ uniq -s 7 -c /root/demo/uniq/tasks.log
2 [INFO] task done
1 [INFO] task failed
第 1、2 行的剩余部分都是 task done,被合并为一组计数 2;第 3 行 task failed 单独一组。输出仍打印组内第一行全文(含被跳过的前缀)。适合「前缀是可变噪音(时间戳、级别),真正要比的是后半段」的日志。
3.9 -f N:跳过每行前 N 个字段再比较
-f 1 跳过第 1 个字段(以空白分隔),access.txt 即忽略日期列:
$ uniq -f 1 -c /root/demo/uniq/access.txt
2 2026-10-15 GET /api
1 2026-10-16 POST /api
1 2026-10-17 GET /api
第 1、2 行跳过日期后都是 GET /api,合并计数 2;第 3 行 POST /api、第 4 行又出现 GET /api(与第 3 行不相邻)各自成组。对照 2.2 节帮助原文「Fields are skipped before chars」:-f 先执行、-s 后执行,两者可叠加(如 uniq -f 1 -s 4 表示跳过 1 个字段再跳 4 个字符)。
💡 提示:uniq 的「字段」定义为「一段空白 + 一段非空白字符」(2.2 节帮助原文),与 awk 的默认分列规则一致;但它不能像 cut 那样用
-d自定义分隔符,逗号数据需先转换或改用 awk。
四、实战示例
前面各节覆盖了单个参数的行为,本节给三个可直接套用的组合场景。
1. 访问频次 TopN——去重计数后交给 sort -rn 倒序:
$ sort /root/demo/uniq/visitors.txt | uniq -c | sort -rn
2 bob
2 alice
1 dave
1 carol
1 bOb
2. 忽略大小写的重复项稽查——数据源大小写不规范时,先归一再找重复:
$ sort /root/demo/uniq/visitors.txt | uniq -i -c
2 alice
3 bOb
1 carol
1 dave
3. 按网段前缀分组统计——-w 把比较范围钉在前 10 个字符上:
$ uniq -w 10 -c /root/demo/uniq/ips.txt
3 192.168.1.101 GET
1 192.168.2.201 GET
五、注意事项与常见问题
本节汇总 uniq 使用中最容易踩坑的四个问题,均对应前文实测结论。
Q1:为什么 uniq 没把重复行去掉?
uniq 只比较相邻行。重复行若不相邻(3.1 节的两个 alice),uniq 看不见它们的「重复关系」。全局去重先 sort 再 uniq;已按时间有序的日志若只想合并连续重复,直接 uniq 反而是对的。
Q2:sort | uniq 与 sort -u 有区别吗?
去重结果等价,sort -u 少一个进程更快。但 uniq 的价值在参数组合:-c 计数、-d/-u 分拣、-i 忽略大小写、-f/-s/-w 收窄比较范围,这些是 sort -u 给不了的。需要统计或分拣时用 sort | uniq 管线,纯去重用 sort -u。
Q3:-f、-s、-w 同时用是什么效果?
按帮助原文约定:先跳字段(-f),再在剩余部分跳字符(-s),最后若有 -w 则把比较长度限制在前 N 个字符内。三者是对「比较范围」的逐级收窄;实际脚本中建议一次只用一个,避免范围计算出错。
Q4:uniq -c 输出的计数字段怎么取?
计数默认按 7 列宽度右对齐( 2 alice),前导是空格。管道后接 awk '{print $1, $2}' 可拆出「次数、内容」两列;需要规整格式时也可用 sort | uniq -c | awk '{print $1}' 单取计数列做后续运算。
六、参考链接
- GNU Coreutils 官方手册 uniq 章节:https://www.gnu.org/software/coreutils/manual/html_node/uniq-invocation.html
- Ubuntu 22.04 (jammy) 在线 man 手册 uniq(1):https://manpages.ubuntu.com/manpages/jammy/en/man1/uniq.1.html
- 本系列关联篇:《sort 命令详解与参数实测(Ubuntu 22.04)》《cut 命令详解与参数实测(Ubuntu 22.04)》