MD5校验对比脚本

  • 始终显示统计摘要(相同、不同、仅存在于 dir1、仅存在于 dir2)
  • 加上 -v 选项时,同时逐行显示每个文件的详细对比状态
  • 保留两种模式:严格对比(路径 + MD5)仅内容对比(忽略路径)
  • 自动处理权限,并给出友好的运行提示

完整脚本:compare_dirs_full.sh

bash 复制代码
#!/bin/bash
set -e

# 显示帮助信息
usage() {
    cat <<EOF
用法: $0 [选项] <目录1> <目录2>

比较两个目录下所有文件的 MD5 值,递归处理子目录。

选项:
  -v, --verbose       显示每个文件的详细对比状态(相同/不同/仅存在)
  -c, --content-only  只比较文件内容(忽略文件名和路径)
  -h, --help          显示此帮助信息

无论是否使用 -v,最后都会输出统计摘要。

示例:
  $0 /root/var/www/html/upload /var/www/html/upload
  $0 -v /root/var/www/html/upload /var/www/html/upload
  $0 -c /root/var/www/html/upload /var/www/html/upload
EOF
    exit 1
}

# 默认参数
verbose=false
content_only=false

# 解析选项
OPTS=$(getopt -o vch --long verbose,content-only,help -n "$0" -- "$@")
if [ $? != 0 ]; then usage; fi
eval set -- "$OPTS"
while true; do
    case "$1" in
        -v|--verbose)      verbose=true; shift ;;
        -c|--content-only) content_only=true; shift ;;
        -h|--help)         usage ;;
        --)                shift; break ;;
        *)                 break ;;
    esac
done

# 检查参数
if [ $# -ne 2 ]; then
    echo "错误:需要两个目录作为参数"
    usage
fi

dir1="$1"
dir2="$2"

# 验证目录存在
if [ ! -d "$dir1" ]; then
    echo "错误:目录 '$dir1' 不存在"
    exit 1
fi
if [ ! -d "$dir2" ]; then
    echo "错误:目录 '$dir2' 不存在"
    exit 1
fi

# 创建临时文件并设置清理
tmp1=$(mktemp)
tmp2=$(mktemp)
trap 'rm -f "$tmp1" "$tmp2"' EXIT

# 生成 MD5 清单:格式为 "MD5  相对路径"
generate_list() {
    local dir="$1"
    local out="$2"
    (cd "$dir" && find . -type f -exec md5sum {} + | sed 's|^\./||' | sort -k2) > "$out"
}

echo "正在生成目录 '$dir1' 的 MD5 清单..."
generate_list "$dir1" "$tmp1"
echo "正在生成目录 '$dir2' 的 MD5 清单..."
generate_list "$dir2" "$tmp2"

# ----- 仅比较内容(忽略文件名) -----
if [ "$content_only" = true ]; then
    echo "--- 仅比较文件内容(忽略文件名) ---"
    # 提取 MD5 值排序后比较
    diff -u <(awk '{print $1}' "$tmp1" | sort) <(awk '{print $1}' "$tmp2" | sort) \
        | sed -e '/^---/d' -e '/^\+\+\+/d' -e '/^@@/d' || true
    
    # 统计:交集(相同MD5)、仅1、仅2
    comm -12 <(awk '{print $1}' "$tmp1" | sort) <(awk '{print $1}' "$tmp2" | sort) | wc -l | xargs printf "\n相同内容文件数(按MD5): %d\n"
    comm -23 <(awk '{print $1}' "$tmp1" | sort) <(awk '{print $1}' "$tmp2" | sort) | wc -l | xargs printf "仅存在于 %s 的内容块数: %d\n" "$dir1"
    comm -13 <(awk '{print $1}' "$tmp1" | sort) <(awk '{print $1}' "$tmp2" | sort) | wc -l | xargs printf "仅存在于 %s 的内容块数: %d\n" "$dir2"
    exit 0
fi

# ----- 严格按路径对比 -----
echo "--- 对比目录结构和文件内容 ---"

# 使用 awk 进行逐行比较,并收集统计信息
awk -v verbose="$verbose" -v d1="$dir1" -v d2="$dir2" '
    function print_diff(path, md5a, md5b) {
        if (md5a == md5b)
            print "相同: " path
        else
            print "不同: " path " ( " d1 ": " md5a ", " d2 ": " md5b " )"
    }
    BEGIN { same=0; diff=0; only1=0; only2=0 }
    NR==FNR {
        # 读取第一个清单
        path = $2; md5[path] = $1; next
    }
    {
        # 读取第二个清单
        path = $2;
        if (path in md5) {
            if (md5[path] == $1) {
                same++;
            } else {
                diff++;
            }
            if (verbose) print_diff(path, md5[path], $1);
            delete md5[path];   # 已匹配
        } else {
            only2++;
            if (verbose) print "仅在 " d2 ": " path " (MD5: " $1 ")"
        }
    }
    END {
        # 剩余在 md5 中的即为仅在 dir1 的文件
        for (path in md5) {
            only1++;
            if (verbose) print "仅在 " d1 ": " path " (MD5: " md5[path] ")"
        }
        # 始终输出统计摘要
        printf "\n=== 统计摘要 ===\n"
        printf "相同文件数(路径和内容均一致): %d\n", same
        printf "内容不同的文件数(路径相同但MD5不同): %d\n", diff
        printf "仅存在于 %s 的文件数: %d\n", d1, only1
        printf "仅存在于 %s 的文件数: %d\n", d2, only2
    }
' "$tmp1" "$tmp2"

exit 0

使用方法

  1. 保存脚本并赋予执行权限

    bash 复制代码
    sudo vi /usr/local/bin/compare_dirs_full.sh   # 或任意位置
    chmod +x /usr/local/bin/compare_dirs_full.sh
  2. 运行(以您的目录为例)

    • 仅显示统计摘要(不输出每个文件的详情)

      bash 复制代码
      sudo compare_dirs_full.sh /root/var/www/html/upload /var/www/html/upload
    • 同时显示每个文件的详细状态 + 统计摘要

      bash 复制代码
      sudo compare_dirs_full.sh -v /root/var/www/html/upload /var/www/html/upload
    • 只比较文件内容(忽略路径)

      bash 复制代码
      sudo compare_dirs_full.sh -c /root/var/www/html/upload /var/www/html/upload

输出示例

-v(部分输出):

复制代码
正在生成目录 '/root/var/www/html/upload' 的 MD5 清单...
正在生成目录 '/var/www/html/upload' 的 MD5 清单...
--- 对比目录结构和文件内容 ---
相同: images/logo.png
不同: config.ini ( /root/var/www/html/upload: d41d8cd98f00b204e9800998ecf8427e , /var/www/html/upload: 098f6bcd4621d373cade4e832627b4f6 )
仅在 /var/www/html/upload: temp/cache.tmp (MD5: 5d41402abc4b2a76b9719d911017c592)

=== 统计摘要 ===
相同文件数(路径和内容均一致): 120
内容不同的文件数(路径相同但MD5不同): 3
仅存在于 /root/var/www/html/upload 的文件数: 5
仅存在于 /var/www/html/upload 的文件数: 2

不加 -v,只会输出摘要部分(不会有详细列表)。


关键改进点

  • 始终输出统计摘要 :无论是否使用 -vawkEND 块都会打印四行统计。
  • 详细模式与摘要并存-v 负责输出每行状态,摘要始终在最后给出。
  • 清晰区分"路径相同但内容不同"和"仅存在某侧",便于定位问题。
  • 临时文件自动清理,支持带空格的文件名。

如果文件数量极大(数十万级)

若目录包含海量文件,生成 MD5 列表可能较慢。此时可以考虑:

  • 使用 parallel 并行计算 MD5,或改用 md5deep 工具(可参考之前的建议)。
  • 但本脚本对于常规规模(几万文件)完全够用。

相关推荐
平生幻3 小时前
Linux 常用命令
linux
ShirleyWang0125 小时前
让headlamp控制台能访问
linux·服务器·python·k8s·k3s
liuccn5 小时前
Linux 存储系统:LVM 与直接分区
linux·运维
捷配链5 小时前
六层服务器板行业材料与工艺标准化选型指南
服务器·制造·pcb
FreeBuf_6 小时前
Windows NT OS内核提权漏洞PoC已公开
windows
雲帝7 小时前
Windows虚拟机UDP大包分片排障
windows·网络协议·udp
xexpertS7 小时前
CI/CD 熔断机制:如何通过编排级熔断器提升开发者效率
java·linux·运维
xuefeiniao7 小时前
CentOS + 宝塔服务器偶发 SSH、面板、网站全部无法访问排查记录(PHP-FPM 内存耗尽案例)
服务器·centos·ssh·php
带娃的IT创业者7 小时前
突破内存瓶颈:利用 Nvidia GPU 显存作为 Linux Swap 空间的深度实践
linux·运维·服务器·nvidia·swap·gpu显存