comm 命令详解与参数实测(Ubuntu 22.04)
已排序文件集合运算工具 comm:三列输出与制表符解读、未排序告警实测、-1/-2/-3 单列抑制、-12/-23/-13 求交集差集对称差、--total 统计与 --output-delimiter 自定义分隔符
comm 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 GNU comm 的已排序文件集合运算能力——默认三列输出(仅文件1、仅文件2、公共行)的制表符排版解读、输入未排序时的告警实测、
-1/-2/-3单列抑制、-12/-23/-13组合求交集/差集/对称差、--total统计汇总、--output-delimiter自定义列分隔符,并对照 sort+uniq 与 diff 说明各自适用差异,全部命令与回显已在 Ubuntu 22.04(GNU coreutils 8.32)实测通过。
一、环境信息
本节列出本文所有实测所依赖的环境与测试数据,便于读者按同规格复现。
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- comm 版本:GNU coreutils 8.32(
comm --version实测) - comm 属于 coreutils 基础工具集,Ubuntu 22.04 默认预装,无需额外安装
- 演示目录:
/root/demo/comm/ - 测试数据:
list_a.txt与list_b.txt为两份已按字典序排序的水果名单(各 5 行,公共行 3 个:banana、cherry、orange);unsorted.txt为故意乱序的文件,用于实测告警行为
二、命令概览
2.1 语法与测试数据准备
comm(common)的基本语法是 comm [OPTION]... FILE1 FILE2:对两个已排序文件逐行做集合运算,缺省输出三列——第 1 列是仅在 FILE1 中出现的行、第 2 列是仅在 FILE2 中出现的行、第 3 列是两个文件共有的行,列与列之间用制表符分隔。它回答的是「两个名单的交集、差集、对称差」,排序是它的前置约定。
💡 技术拓展:对两份名单求交集/差集不止 comm 一种做法,横向对比——
方法 前置要求 输出形态 适用场景 comm -12 / -23 / -3 两输入均已排序 三列可抑制,一次给全三种关系 名单对账、求交差集 sort + uniq -d / -u 先合并排序 重复行 / 唯一行 只关心有无重复 grep -Fxf / -Fvxf 文本文件即可 直接过滤行 从大名单中筛除黑名单 diff 无 行级增删编辑指令 关注「怎么改」而非集合关系 comm 的优势是三列一次算全且列语义固定,代价是必须先排序;本文以 comm 为主线实测。
准备两份已排序名单(用 sort 保证排序,这也是生产上的推荐生成方式),并准备一份乱序文件用于 3.3 节告警实测:
rm -rf /root/demo/comm && mkdir -p /root/demo/comm
printf 'apple\nbanana\ncherry\ngrape\norange\n' | sort > /root/demo/comm/list_a.txt
printf 'banana\ncherry\nkiwi\nlemon\norange\n' | sort > /root/demo/comm/list_b.txt
printf 'zebra\napple\nmango\n' > /root/demo/comm/unsorted.txt
验证两份名单内容(apple/grape 仅在 a,kiwi/lemon 仅在 b,banana/cherry/orange 公共):
$ cat -n /root/demo/comm/list_a.txt
1 apple
2 banana
3 cherry
4 grape
5 orange
$ cat -n /root/demo/comm/list_b.txt
1 banana
2 cherry
3 kiwi
4 lemon
5 orange
2.2 --help 全量回显
comm --help 输出全部参数清单(实测原样保留,可作为参数速查表):
Usage: comm [OPTION]... FILE1 FILE2
Compare sorted files FILE1 and FILE2 line by line.
When FILE1 or FILE2 (not both) is -, read standard input.
With no options, produce three-column output. Column one contains
lines unique to FILE1, column two contains lines unique to FILE2,
and column three contains lines common to both files.
-1 suppress column 1 (lines unique to FILE1)
-2 suppress column 2 (lines unique to FILE2)
-3 suppress column 3 (lines that appear in both files)
--check-order check that the input is correctly sorted, even
if all input lines are pairable
--nocheck-order do not check that the input is correctly sorted
--output-delimiter=STR separate columns with STR
--total output a summary
-z, --zero-terminated line delimiter is NUL, not newline
--help display this help and exit
--version output version information and exit
Note, comparisons honor the rules specified by 'LC_COLLATE'.
Examples:
comm -12 file1 file2 Print only lines present in both file1 and file2.
comm -3 file1 file2 Print lines in file1 not in file2, and vice versa.
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/comm>
or available locally via: info '(coreutils) comm invocation'
帮助原文明确三列语义与三个抑制参数;Examples 段直接给出 -12(交集)与 -3(对称差)两个经典组合;When FILE1 or FILE2 (not both) is - 说明单个输入可以是标准输入流;比较规则遵循 LC_COLLATE(排序规则与 locale 相关,见第五节 Q5)。
2.3 版本确认
确认实测环境的 comm 版本(coreutils 全家桶统一版本号):
$ comm --version | head -1
comm (GNU coreutils) 8.32
三、参数逐项实测
3.1 默认三列输出
不加参数时,comm 对两份排序名单输出三列。实测 list_a.txt 与 list_b.txt:
$ comm /root/demo/comm/list_a.txt /root/demo/comm/list_b.txt
apple
banana
cherry
grape
kiwi
lemon
orange
列的归属靠行首制表符个数区分:无制表符(apple、grape)在第 1 列——仅文件1;一个制表符(kiwi、lemon)在第 2 列——仅文件2;两个制表符(banana、cherry、orange)在第 3 列——公共行。管道接 cat -A(^I 是制表符的显形、$ 是行尾)验证排版字节:
$ comm /root/demo/comm/list_a.txt /root/demo/comm/list_b.txt | cat -A
apple$
^I^Ibanana$
^I^Icherry$
grape$
^Ikiwi$
^Ilemon$
^I^Iorange$
💡 说明:三列输出的行首制表符在终端里表现为缩进,肉眼容易误读成「层级」。确认某行属于哪一列,用
cat -A数^I个数最可靠;后续的-N参数就是把对应列连同其制表符一起删掉。
3.2 未排序输入的告警实测
comm 的三列结果依赖排序前提,输入乱序时它会发出告警,且结果不可信。实测用乱序文件与 list_b.txt 比较:
$ comm /root/demo/comm/unsorted.txt /root/demo/comm/list_b.txt
banana
cherry
kiwi
lemon
orange
zebra
comm: file 1 is not in sorted order
apple
mango
comm: input is not in sorted order
解读:两条告警 comm: file 1 is not in sorted order(第一次发现文件1乱序)与 comm: input is not in sorted order(整体输入被判定乱序)都出现在标准错误上,混杂在三列输出之间;apple、mango 本该与公共行比较位置,却因乱序被甩到末尾。comm 不会因此退出失败(不阻断管道),但集合关系已失真——生产上务必保证输入有序,或在管道前统一 sort。
3.3 -1 / -2 / -3:单列抑制
-N 表示「去掉第 N 列」。-1 抑制仅文件1的列,剩「仅文件2 + 公共」两列:
$ comm -1 /root/demo/comm/list_a.txt /root/demo/comm/list_b.txt
banana
cherry
kiwi
lemon
orange
-2 抑制仅文件2的列,剩「仅文件1 + 公共」两列:
$ comm -2 /root/demo/comm/list_a.txt /root/demo/comm/list_b.txt
apple
banana
cherry
grape
orange
-3 抑制公共列,只剩两个文件各自独有的行(公共行 banana、cherry、orange 消失):
$ comm -3 /root/demo/comm/list_a.txt /root/demo/comm/list_b.txt
apple
grape
kiwi
lemon
💡 说明:抑制后剩余行保留原列位缩进(如
-1后 kiwi 无制表符、banana 有一个),行首制表符的个数继续标示该行来自哪一列。真正只要内容、不要缩进时,用 3.5 节的组合参数一次抑制到位。
3.4 -12 / -23 / -13:组合求交集、差集、对称差
两个抑制参数叠加,剩余的那一列就是对应的集合运算结果。-12 去掉两个独有列、只留公共列——交集(banana、cherry、orange):
$ comm -12 /root/demo/comm/list_a.txt /root/demo/comm/list_b.txt
banana
cherry
orange
-23 去掉仅文件2列与公共列,只剩仅文件1的行——A−B 差集(apple、grape):
$ comm -23 /root/demo/comm/list_a.txt /root/demo/comm/list_b.txt
apple
grape
-13 去掉仅文件1列与公共列,只剩仅文件2的行——B−A 差集(kiwi、lemon):
$ comm -13 /root/demo/comm/list_a.txt /root/demo/comm/list_b.txt
kiwi
lemon
-3 则保留两个独有列——对称差(A−B 加 B−A)。帮助原文的 Examples 段正是以 comm -12(交集)与 comm -3(对称差)作示例。
3.5 --total:统计汇总
--total 在三列输出的末尾追加一行汇总,依次给出三列的行数与 total 字样。实测:
$ comm --total /root/demo/comm/list_a.txt /root/demo/comm/list_b.txt
apple
banana
cherry
grape
kiwi
lemon
orange
2 2 3 total
汇总行 2 2 3 total 读作:仅文件1有 2 行(apple、grape)、仅文件2有 2 行(kiwi、lemon)、公共 3 行(banana、cherry、orange)。对账场景中,先看这行数字就能判断两边规模差在哪一侧,不必逐行数。
3.6 --output-delimiter:自定义列分隔符
默认列分隔是制表符,--output-delimiter=STR 可换成任意字符串。实测换成竖线 |:
$ comm --output-delimiter='|' /root/demo/comm/list_a.txt /root/demo/comm/list_b.txt
apple
||banana
||cherry
grape
|kiwi
|lemon
||orange
解读:分隔符插在「空列的位置」——第 2 列的行输出 |kiwi(1 个分隔符),第 3 列的行输出 ||banana(2 个分隔符),第 1 列的行(apple、grape)不带分隔符。导出 CSV 类格式或交给不支持制表符的下游处理时用它。
四、实战示例
4.1 对两条命令的输出求交集
comm 的输入可以是标准输入(帮助原文:When FILE1 or FILE2 (not both) is -),配合 Bash 进程替换 <(命令),两条命令的输出无需落盘即可求交。实测对两份名单各自 sort 后求交集:
$ comm -12 <(sort /root/demo/comm/list_a.txt) <(sort /root/demo/comm/list_b.txt)
banana
cherry
orange
结果与 3.4 节对已排序文件直接求交集一致。实战中把 sort 换成 sort /etc/passwd、ps 输出、包清单等,即可对任意两条命令的结果做集合运算。
五、注意事项与常见问题
Q1:为什么 comm 一定要输入已排序? comm 用「双指针同步推进」的算法比较两份名单:两列行首字符相等才判为公共行,指针各进一步;一旦乱序,同步关系就断裂(3.2 节实测 apple、mango 被甩到输出末尾)。与 diff 的完整比对算法不同,comm 的线性扫描以排序为正确性前提,这也是它快的原因。
Q2:看到 comm: file 1 is not in sorted order 告警,输出还能用吗?
不能当作正确的集合结果使用。告警只提示失真、不阻止输出,也不改变退出码。补救方式就是先排序再比较:comm -12 <(sort a) <(sort b),或提前对两份名单统一 sort 后落盘。
Q3:comm、sort+uniq、diff 三者怎么选?
看问题类型:「两份名单的交集/差集」用 comm(3.4 节的 -12/-23/-13 就是为这组问题设计的);「一份输入里的重复与唯一」用 sort 加 uniq;「两个文件怎么从 A 改成 B」用 diff。comm 要求排序、diff 无前提、uniq 是单输入工具,前提与问题匹配即可。
Q4:列间的制表符导致下游切割出错怎么办?
两种办法:一是按 3.6 节实测用 --output-delimiter 换成确定的单字符分隔符再交下游;二是组合参数(如 -12、-23)让输出只剩单列,天然无制表符。用 cut/awk 处理 comm 原始三列输出时,注意空列也占一个制表符位。
Q5:同样的输入换个环境,公共行为什么变了?
comm 的行比较遵循 LC_COLLATE(帮助原文:comparisons honor the rules specified by 'LC_COLLATE'),不同 locale 的字典序不同(如忽略大小写、重音展开),sort 与 comm 必须使用同一 locale 才能配对。固定 LC_ALL=C 再排序再比较,是最稳妥的跨环境做法。
六、参考链接
- GNU coreutils 官方手册(comm 章节):https://www.gnu.org/software/coreutils/manual/html_node/comm-invocation.html
- GNU coreutils 首页:https://www.gnu.org/software/coreutils/comm
- 本机帮助:
comm --help、man comm(Ubuntu 22.04 内置)