linux comm命令

comm命令 是Linux系统中用于比较两个已排序文件的工具,可以显示两个文件中的共同行和特有行。

命令简介

comm命令逐行比较两个已排序的文件,并生成三列输出:第一列显示只在第一个文件中出现的行,第二列显示只在第二个文件中出现的行,第三列显示两个文件中都存在的行。这对于分析文件差异、数据去重等场景非常有用。

语法格式

comm [选项] 文件1 文件2

常用选项

选项 说明
-1 不输出第一列(文件1特有的行)
-2 不输出第二列(文件2特有的行)
-3 不输出第三列(两个文件共有的行)
--check-order 检查输入文件是否已排序,如果没有排序则报错
--nocheck-order 不检查输入文件是否已排序(默认行为)
--output-delimiter=STR 使用指定字符串作为列分隔符
--help 显示帮助信息
--version 显示版本信息

输出格式说明

comm命令的输出默认分为三列,用制表符分隔:

第1列        第2列        第3列
(文件1特有)  (文件2特有)  (两个文件共有)

使用示例

示例1:基本使用 - 比较两个排序文件

比较两个已排序的文件:

# 创建两个已排序的测试文件
echo -e "apple\nbanana\ncherry\ndate\nelderberry" > file1.txt
echo -e "banana\ncherry\ndate\nfig\ngrape" > file2.txt

# 查看文件内容
echo "文件1内容:"
cat file1.txt
echo -e "\n文件2内容:"
cat file2.txt

# 使用comm比较文件
echo -e "\ncomm比较结果:"
comm file1.txt file2.txt

输出结果:

apple
                banana
                cherry
                date
        elderberry
        fig
        grape

示例2:控制输出列

使用选项控制显示哪些列:

# 只显示两个文件共有的行
comm -12 file1.txt file2.txt

# 只显示文件1特有的行
comm -23 file1.txt file2.txt

# 只显示文件2特有的行
comm -13 file1.txt file2.txt

# 显示文件1特有和文件2特有的行(不显示共有行)
comm -3 file1.txt file2.txt | sed 's/\t//g'

示例3:处理未排序文件

对未排序的文件先进行排序再比较:

# 创建未排序的文件
echo -e "banana\napple\ncherry" > unsorted1.txt
echo -e "cherry\napple\ndate" > unsorted2.txt

# 直接比较未排序文件(结果可能不正确)
comm unsorted1.txt unsorted2.txt

# 先排序再比较
comm <(sort unsorted1.txt) <(sort unsorted2.txt)

# 或者保存排序后的文件
sort unsorted1.txt > sorted1.txt
sort unsorted2.txt > sorted2.txt
comm sorted1.txt sorted2.txt

示例4:自定义分隔符

使用自定义分隔符代替制表符:

# 使用逗号作为分隔符
comm --output-delimiter=',' file1.txt file2.txt

# 使用管道符号作为分隔符
comm --output-delimiter=' | ' file1.txt file2.txt

# 无分隔符(合并列)
comm --output-delimiter='' file1.txt file2.txt | column -t

示例5:实际应用 - 比较用户列表

比较两个系统的用户列表:

# 获取两个系统的用户列表(已排序)
cat /etc/passwd | cut -d: -f1 | sort > users1.txt
# 假设这是另一个系统的用户列表
cat /etc/passwd | cut -d: -f1 | sort | head -20 > users2.txt

# 比较用户列表
echo "只在系统1中的用户:"
comm -23 users1.txt users2.txt

echo -e "\n只在系统2中的用户:"
comm -13 users1.txt users2.txt

echo -e "\n两个系统共有的用户:"
comm -12 users1.txt users2.txt

示例6:数据分析和处理

使用comm进行数据分析:

# 创建测试数据
echo -e "1001\n1002\n1003\n1004\n1005" > old_customers.txt
echo -e "1002\n1003\n1005\n1006\n1007" > new_customers.txt

# 分析客户变化
echo "流失的客户:"
comm -23 old_customers.txt new_customers.txt

echo -e "\n新增的客户:"
comm -13 old_customers.txt new_customers.txt

echo -e "\n保留的客户:"
comm -12 old_customers.txt new_customers.txt

实际应用场景

场景1:系统配置比较

比较两个系统的配置文件差异:

#!/bin/bash

# 比较两个服务器的服务列表
compare_services() {
    local server1=$1
    local server2=$2

    # 获取服务列表(已排序)
    ssh "$server1" "systemctl list-unit-files --type=service | grep enabled | cut -d' ' -f1 | sort" > services1.txt
    ssh "$server2" "systemctl list-unit-files --type=service | grep enabled | cut -d' ' -f1 | sort" > services2.txt

    echo "只在 $server1 中启用的服务:"
    comm -23 services1.txt services2.txt

    echo -e "\n只在 $server2 中启用的服务:"
    comm -13 services1.txt services2.txt

    # 清理临时文件
    rm services1.txt services2.txt
}

# 使用函数
compare_services "server1" "server2"

场景2:代码版本比较

比较不同版本代码中的函数列表:

#!/bin/bash

# 提取C/C++项目中的函数名
extract_functions() {
    local source_file=$1
    local output_file=$2

    # 简单的函数提取(实际应用可能需要更复杂的正则表达式)
    grep -E '^[a-zA-Z_][a-zA-Z0-9_]*[[:space:]]+[a-zA-Z_][a-zA-Z0-9_]*\(' "$source_file" | \
    sed 's/{.*//' | \
    sort > "$output_file"
}

# 比较两个版本
extract_functions "version1.c" "funcs1.txt"
extract_functions "version2.c" "funcs2.txt"

echo "新增的函数:"
comm -13 funcs1.txt funcs2.txt

echo -e "\n删除的函数:"
comm -23 funcs1.txt funcs2.txt

场景3:数据库记录比较

比较数据库导出的记录:

#!/bin/bash

# 比较两个时间点的数据导出
compare_data_exports() {
    local export1=$1
    local export2=$2

    # 确保文件已排序
    sort "$export1" > "${export1}.sorted"
    sort "$export2" > "${export2}.sorted"

    echo "新增的记录:"
    comm -13 "${export1}.sorted" "${export2}.sorted" | head -10

    echo -e "\n删除的记录:"
    comm -23 "${export1}.sorted" "${export2}.sorted" | head -10

    echo -e "\n变更统计:"
    echo "新增记录数: $(comm -13 "${export1}.sorted" "${export2}.sorted" | wc -l)"
    echo "删除记录数: $(comm -23 "${export1}.sorted" "${export2}.sorted" | wc -l)"
    echo "共同记录数: $(comm -12 "${export1}.sorted" "${export2}.sorted" | wc -l)"

    # 清理临时文件
    rm "${export1}.sorted" "${export2}.sorted"
}

# 使用函数
compare_data_exports "data_20230101.txt" "data_20230102.txt"

高级用法

处理大型文件

使用进程替换处理大型文件,避免创建临时文件:

# 使用进程替换,不创建临时文件
comm <(sort large_file1.txt) <(sort large_file2.txt)

# 结合其他命令处理输出
comm <(sort file1.txt) <(sort file2.txt) | grep -v '^$' | column -t

与diff命令的比较

comm和diff命令的区别:

# comm - 逐行比较已排序文件,显示三列结果
comm file1.txt file2.txt

# diff - 显示文件差异,包括行内容变化
diff file1.txt file2.txt

# 使用diff -u获得更易读的差异
diff -u file1.txt file2.txt

结合awk进行高级处理

使用awk处理comm的输出:

# 为不同列添加标签
comm file1.txt file2.txt | awk '
    /^\t\t/ {print "共同: " $3}
    /^\t[^\t]/ {print "文件2特有: " $2}
    /^[^\t]/ {print "文件1特有: " $1}
'

# 统计各类行数
comm file1.txt file2.txt | awk '
    /^\t\t/ {common++}
    /^\t[^\t]/ {file2_only++}
    /^[^\t]/ {file1_only++}
    END {
        print "文件1特有: " file1_only
        print "文件2特有: " file2_only
        print "共同: " common
    }
'

注意事项

  • 输入文件必须已排序 - 这是comm命令最重要的前提条件
  • 默认不检查文件是否已排序,使用--check-order选项可以强制检查
  • 对于大型文件,先排序可能消耗较多时间和内存
  • 输出中的制表符可能在不同终端中显示效果不同
  • 空行也会被比较,可以使用grep过滤空行
  • comm按字节比较,对于大小写敏感
  • 可以使用进程替换避免创建临时排序文件

常见问题解决

确保输入文件已正确排序:

# 方法1:使用sort命令排序
sort file1.txt > file1_sorted.txt
sort file2.txt > file2_sorted.txt
comm file1_sorted.txt file2_sorted.txt

# 方法2:使用进程替换
comm <(sort file1.txt) <(sort file2.txt)

# 方法3:忽略排序检查(不推荐)
comm --nocheck-order file1.txt file2.txt

处理制表符显示问题:

# 方法1:使用自定义分隔符
comm --output-delimiter=" | " file1.txt file2.txt

# 方法2:使用column命令格式化输出
comm file1.txt file2.txt | column -t

# 方法3:使用awk重新格式化
comm file1.txt file2.txt | awk -F'\t' '{
    printf "%-20s %-20s %-20s\n", $1, $2, $3
}'

确保排序和比较时正确处理空格:

# 使用稳定排序
sort -s file1.txt > file1_sorted.txt
sort -s file2.txt > file2_sorted.txt

# 或者忽略前导空格排序
sort -b file1.txt > file1_sorted.txt
sort -b file2.txt > file2_sorted.txt

# 比较时考虑整个行内容
comm file1_sorted.txt file2_sorted.txt

相关命令

命令 说明 适用场景
diff 显示文件差异 需要详细差异信息,包括修改的内容
sort 文件排序 为comm准备输入文件
uniq 报告或忽略重复行 处理单个文件的重复行
join 基于共同字段连接两个文件 基于关键字段合并文件
grep 文本搜索 基于模式过滤文件内容