comm 命令详解与参数实测(Ubuntu 22.04)

已排序文件集合运算工具 comm:三列输出与制表符解读、未排序告警实测、-1/-2/-3 单列抑制、-12/-23/-13 求交集差集对称差、--total 统计与 --output-delimiter 自定义分隔符

Linux命令Linux系统UbuntuUbuntu 22.04命令行文本处理2026/10/15

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 再排序再比较,是最稳妥的跨环境做法。

六、参考链接