DupFinder:一个用 Rust 编写的高性能重复文件查找工具

DupFinder:一个用 Rust 编写的高性能重复文件查找工具

📌 前言

在日常工作和生活中,我们的电脑里经常会积累大量重复文件------下载的重复照片、备份的文档副本、多次保存的视频文件等。这些重复文件不仅占用宝贵的磁盘空间,还让文件管理变得混乱。

今天给大家介绍一个我用 Rust 编写的重复文件查找工具 DupFinder ,它不仅速度快、准确率高,还支持 JSON 导出安全删除脚本生成,非常适合需要自动化处理或批量清理重复文件的场景。

项目地址


🎯 项目背景

为什么要写这个工具?

市面上已经有一些重复文件查找工具,比如:

  • fdupes (C 语言):经典工具,但缺少现代化的功能(无 JSON 输出、无彩色界面)
  • rdfind (C++):速度快但准确性稍低,且只支持 Linux

我希望开发一个:

  1. 准确性 100% 的工具(4 层验证)
  2. 支持 JSON 输出,方便自动化和 CI/CD 集成
  3. 生成安全的删除脚本,而不是直接删除
  4. 友好的彩色界面
  5. 跨平台支持(Linux、macOS、Windows)

于是 DupFinder 诞生了!

技术选型:为什么选择 Rust?

  • 性能优异:接近 C/C++ 的性能
  • 🔒 内存安全:无 GC,无内存泄漏,无数据竞争
  • 📦 易于分发:静态链接,单个二进制文件
  • 🛠️ 优秀的工具链:Cargo 包管理,强大的类型系统

✨ 核心功能

1. 四层验证机制(100% 准确)

DupFinder 采用渐进式的 4 层验证策略,确保查找结果 100% 准确:

复制代码
第 1 层:文件大小比较
    ↓ (快速排除大小不同的文件)
第 2 层:部分内容哈希 (前 8KB 的 MD5)
    ↓ (排除开头不同的文件)
第 3 层:完整文件 MD5
    ↓ (排除内容不同的文件)
第 4 层:逐字节比较
    ↓ (最终确认 100% 相同)
✅ 确认为重复文件

为什么需要 4 层验证?

  • 性能优化:大部分文件在前 1-2 层就被排除,避免不必要的 MD5 计算
  • 避免哈希碰撞:虽然 MD5 碰撞概率极低,但逐字节比较确保 100% 准确
  • 处理硬链接:检测同一 inode 的文件,避免误报

2. JSON 输出(独家功能)

与 fdupes 和 rdfind 不同,DupFinder 支持导出结构化的 JSON 报告:

bash 复制代码
dupfinder ~/Downloads --json report.json

JSON 格式示例

json 复制代码
{
  "scan_info": {
    "base_path": "/home/user/Downloads",
    "total_groups": 2,
    "timestamp": "2025-12-02T16:30:00+08:00"
  },
  "duplicate_groups": [
    {
      "group_id": 1,
      "file_size": 1048576,
      "file_count": 3,
      "md5_hash": "5d41402abc4b2a76b9719d911017c592",
      "files": [
        {
          "path": "/home/user/Downloads/file1.txt",
          "absolute_path": "/home/user/Downloads/file1.txt"
        }
      ]
    }
  ],
  "statistics": {
    "total_duplicate_files": 5,
    "deletable_files": 3,
    "potential_space_savings": 3145728
  }
}

应用场景

  • ✅ CI/CD 集成:自动检测代码仓库中的重复文件
  • ✅ 自动化脚本:用 Python/Node.js 解析 JSON 进行批处理
  • ✅ 数据分析:统计重复文件的分布情况

3. 安全删除脚本生成(独家功能)

DupFinder 不会直接删除文件,而是生成一个可编辑的 Bash 删除脚本:

bash 复制代码
dupfinder ~/Downloads --delete-script delete.sh

脚本特点

  • ✅ 需要手动输入 yes 确认,防止误删
  • ✅ 详细的注释,清楚标注保留和删除的文件
  • ✅ 可以手动编辑脚本,选择保留哪个文件
  • ✅ 完整的错误处理和统计信息

生成的脚本示例

bash 复制代码
#!/bin/bash
# 保留: /home/user/Downloads/photo.jpg
# 删除文件 1/2
if [ -f "/home/user/Downloads/photo_copy.jpg" ]; then
    echo "删除: /home/user/Downloads/photo_copy.jpg"
    rm "/home/user/Downloads/photo_copy.jpg"
fi

4. 其他实用功能

  • 🎨 彩色输出:清晰易读的终端界面
  • 📊 详细模式:显示 4 层验证的完整过程
  • 💾 空间统计:显示可节省的磁盘空间
  • 🔗 硬链接检测:智能识别并跳过硬链接(可选)
  • 📍 路径显示:支持绝对路径或相对路径
  • 🔄 递归扫描:默认扫描所有子目录

📦 安装方法

方式 1:从 crates.io 安装(推荐)

如果你已经安装了 Rust,可以直接使用 Cargo 安装:

bash 复制代码
cargo install dupfinder

优点

  • 自动编译优化版本
  • 自动安装到 ~/.cargo/bin
  • 容易更新:cargo install dupfinder --force

方式 2:从 GitHub Release 下载

访问 Releases 页面,下载适合你系统的二进制文件:

  • dupfinder-linux-x86_64 - Linux (GNU)
  • dupfinder-linux-x86_64-musl - Linux (静态链接)
  • dupfinder-macos-x86_64 - macOS (Intel)
  • dupfinder-macos-aarch64 - macOS (Apple Silicon)
  • dupfinder-windows-x86_64.exe - Windows

Linux/macOS 安装

bash 复制代码
# 下载后赋予执行权限
chmod +x dupfinder-linux-x86_64

# 移动到 PATH 目录
sudo mv dupfinder-linux-x86_64 /usr/local/bin/dupfinder

方式 3:从源码编译

bash 复制代码
# 克隆仓库
git clone https://github.com/Waitfish/dupfinder.git
cd dupfinder

# 编译(Release 模式)
cargo build --release

# 安装
sudo cp target/release/dupfinder /usr/local/bin/

验证安装

bash 复制代码
dupfinder --version
dupfinder --help

🚀 使用方法

基本使用

1. 扫描当前目录
bash 复制代码
# 递归扫描当前目录及所有子目录(默认)
dupfinder

# 或指定目录
dupfinder ~/Downloads

输出示例

复制代码
🔍 DupFinder - 重复文件查找工具
📂 扫描路径: /home/user/Downloads
🔄 递归模式: 开启

🔎 开始扫描 1234 个文件...

======================================================================
📊 发现 3 组重复文件
======================================================================

组 1:
  /home/user/Downloads/photo1.jpg
  /home/user/Downloads/backup/photo1.jpg

组 2:
  /home/user/Downloads/video.mp4
  /home/user/Downloads/copy_video.mp4
  /home/user/Downloads/video_backup.mp4

======================================================================
📈 统计信息:
  总重复文件数: 5
  可删除文件数: 3 (保留每组 1 个)
======================================================================
2. 显示详细信息
bash 复制代码
dupfinder -v -S ~/Downloads

参数说明

  • -v--verbose:显示 4 层验证的详细过程
  • -S--size:显示文件大小和可节省空间

输出示例

复制代码
🔍 第 1 层:按文件大小分组...
  ✓ 找到 15 组可能重复的文件(50 个文件)
🔍 第 2 层:计算部分内容哈希...
  ✓ 检查了 50 个文件,找到 8 组部分哈希相同(20 个文件)
🔍 第 3 层:计算完整文件 MD5...
  ✓ 检查了 20 个文件,找到 5 组完整 MD5 相同(15 个文件)
🔍 第 4 层:逐字节比较验证...
  ↪ 跳过硬链接: file1 <-> file1_link
  ✓ 进行了 10 次字节比较,确认 5 组完全重复(15 个文件)

组 1:
  文件大小: 1.5 MB
  /home/user/Downloads/photo1.jpg
  /home/user/Downloads/photo2.jpg

可节省空间: 10.5 MB (10485760 bytes)
3. 只扫描当前目录(不递归)
bash 复制代码
dupfinder -n ~/Downloads

参数 -n--no-recursive 表示不递归扫描子目录。

4. 使用相对路径显示
bash 复制代码
dupfinder -R ~/project

参数 -R--relative 显示相对路径,方便查看项目结构。

高级功能

1. 导出 JSON 报告
bash 复制代码
dupfinder ~/Downloads --json report.json

使用 jq 解析 JSON

bash 复制代码
# 查看统计信息
cat report.json | jq '.statistics'

# 查看可节省空间
cat report.json | jq '.statistics.potential_space_savings'

# 查看所有重复组
cat report.json | jq '.duplicate_groups[]'

Python 处理示例

python 复制代码
import json

with open('report.json') as f:
    data = json.load(f)

print(f"扫描路径: {data['scan_info']['base_path']}")
print(f"重复组数: {data['scan_info']['total_groups']}")
print(f"可节省空间: {data['statistics']['potential_space_savings'] / 1024 / 1024:.2f} MB")

for group in data['duplicate_groups']:
    print(f"\n组 {group['group_id']}:")
    for file in group['files']:
        print(f"  - {file['path']}")
2. 生成删除脚本
bash 复制代码
# 生成删除脚本
dupfinder ~/Downloads --delete-script delete_dups.sh

# 查看脚本内容
cat delete_dups.sh

# 执行删除(需要确认)
bash delete_dups.sh

执行效果

复制代码
⚠️  警告: 即将删除重复文件!
扫描路径: /home/user/Downloads
重复组数: 3
将删除文件数: 5
可节省空间: 10.5 MB

确认要继续吗? (yes/no): yes
删除: /home/user/Downloads/photo_copy.jpg
删除: /home/user/Downloads/video_backup.mp4
...
✅ 成功删除: 5 个文件
💾 节省空间: 10.50MB
3. 同时生成 JSON 和删除脚本
bash 复制代码
dupfinder -v -S ~/Downloads \
    --json report.json \
    --delete-script delete_dups.sh

这样可以先查看 JSON 报告,再决定是否执行删除。

完整参数列表

参数 简写 说明 默认值
<path> - 扫描目录 . (当前目录)
--recursive -r 递归扫描 开启
--no-recursive -n 不递归 -
--verbose -v 详细模式 关闭
--size -S 显示大小 关闭
--relative -R 相对路径 关闭
--hardlinks -H 包含硬链接 关闭
--json <FILE> - JSON 输出 -
--delete-script <FILE> - 生成删除脚本 -

🎯 实际应用场景

场景 1:清理下载文件夹

bash 复制代码
# 查看可以删除多少重复文件
dupfinder -S ~/Downloads

# 生成删除脚本
dupfinder ~/Downloads --delete-script clean_downloads.sh

# 检查并执行
bash clean_downloads.sh

场景 2:代码仓库去重

bash 复制代码
cd ~/my-project

# 使用相对路径查看
dupfinder -R .

# 导出报告
dupfinder . --json dup_report.json

场景 3:备份文件检查

bash 复制代码
# 详细模式查看验证过程
dupfinder -v ~/backup

# 同时生成报告和删除脚本
dupfinder ~/backup --json backup_report.json --delete-script clean_backup.sh

场景 4:CI/CD 集成

bash 复制代码
#!/bin/bash
# 在 CI 中检查重复文件

dupfinder . --json dup_report.json

# 解析 JSON
deletable=$(jq '.statistics.deletable_files' dup_report.json)

if [ "$deletable" -gt 0 ]; then
    echo "⚠️  发现 $deletable 个重复文件,请清理"
    jq '.duplicate_groups[]' dup_report.json
    exit 1
fi

echo "✅ 未发现重复文件"

场景 5:定期清理任务

bash 复制代码
# 创建清理脚本 weekly_cleanup.sh
#!/bin/bash
dupfinder ~/Downloads --delete-script /tmp/cleanup.sh --json /tmp/report.json

# 发送报告邮件
mail -s "重复文件清理报告" user@example.com < /tmp/report.json

# 可选:自动执行(谨慎使用!)
# echo "yes" | bash /tmp/cleanup.sh

# 添加到 crontab(每周日凌晨 2 点)
# 0 2 * * 0 /home/user/weekly_cleanup.sh

🔧 技术实现

项目结构

复制代码
dupfinder/
├── Cargo.toml          # 项目配置和依赖
├── src/
│   └── main.rs         # 主程序(803 行)
├── README.md           # 项目文档
├── USAGE.md            # 详细使用说明
├── CHEATSHEET.md       # 快速参考
└── .github/workflows/  # CI/CD 配置

核心依赖

toml 复制代码
[dependencies]
clap = { version = "4.4", features = ["derive"] }  # 命令行参数解析
walkdir = "2.4"           # 目录遍历
md5 = "0.7"               # MD5 哈希计算
colored = "2.1"           # 彩色输出
same-file = "1.0"         # 硬链接检测
serde = { version = "1.0", features = ["derive"] }  # JSON 序列化
serde_json = "1.0"        # JSON 支持
chrono = "0.4"            # 时间处理

关键数据结构

rust 复制代码
#[derive(Debug, Clone, Serialize, Deserialize)]
struct FileInfo {
    path: PathBuf,              // 文件路径
    size: u64,                  // 文件大小
    partial_hash: Option<String>,  // 部分哈希(8KB)
    full_hash: Option<String>,     // 完整 MD5
}

struct DupFinder {
    verbose: bool,              // 详细模式
    show_size: bool,            // 显示大小
    include_hardlinks: bool,    // 包含硬链接
    relative_path: bool,        // 相对路径
    base_path: PathBuf,         // 基准路径
}

性能优化

  1. 分层筛选:大部分文件在前 1-2 层就被排除
  2. 按需计算:只对潜在重复文件计算哈希
  3. 缓冲读取:使用 8KB 缓冲区减少 I/O
  4. 编译优化 :使用 opt-level = "z"lto = true

Cargo.toml 优化配置

toml 复制代码
[profile.release]
strip = true       # 去掉符号表
opt-level = "z"    # 优化体积
lto = true         # 链接时优化
codegen-units = 1  # 增加优化机会

最终二进制大小:758KB(优化后)


📊 性能对比

测试环境:1000 个文件,总大小 500MB

工具 时间 准确性 功能
dupfinder 2.3s 100% JSON、删除脚本、彩色输出
fdupes 2.5s 100% 交互式删除
rdfind 1.8s 99.9% 自动删除
纯 MD5 3.2s 99.9% -

🆚 与其他工具对比

特性 dupfinder fdupes rdfind
语言 Rust 🦀 C C++
4 层验证
JSON 输出
删除脚本 ✅ (自动)
彩色输出
硬链接检测
跨平台 Linux
二进制大小 758KB ~50KB ~100KB

DupFinder 的优势

  • ✅ 唯一支持 JSON 输出的工具
  • ✅ 生成安全的删除脚本(可编辑、需确认)
  • ✅ 现代化的彩色界面
  • ✅ 完整的文档和示例

💡 Rust 学习心得

作为一个 Rust 学习项目,DupFinder 让我深入理解了:

1. 所有权和借用

rust 复制代码
// 借用路径,不获取所有权
fn calculate_md5(&self, path: &Path) -> io::Result<String> {
    let mut file = File::open(path)?;  // path 是借用的
    // ...
}

2. 错误处理

rust 复制代码
// Result<T, E> 强制处理错误
fn find_duplicates(&self, root: &Path) -> Vec<Vec<FileInfo>> {
    match fs::metadata(path) {
        Ok(metadata) => { /* 处理 */ },
        Err(e) => { /* 错误处理 */ }
    }
}

3. 迭代器和函数式编程

rust 复制代码
// filter_map, map 等零成本抽象
let paths: Vec<_> = WalkDir::new(root)
    .into_iter()
    .filter_map(|e| e.ok())
    .filter(|e| e.file_type().is_file())
    .map(|e| e.path().to_path_buf())
    .collect();

4. 序列化

rust 复制代码
// 使用 serde 自动序列化
#[derive(Serialize, Deserialize)]
struct FileInfo {
    path: PathBuf,
    size: u64,
}

let json = serde_json::to_string_pretty(&report)?;

📝 总结

DupFinder 是一个功能完整、性能优异的重复文件查找工具,特别适合:

✅ 需要自动化处理的场景(JSON 输出)

✅ 需要安全审查的场景(删除脚本)

✅ 需要跨平台支持的场景

✅ 追求准确性的场景(4 层验证)

下一步计划

  • 交互式删除模式
  • 并行处理大文件
  • 进度条显示
  • 更多哈希算法(SHA256, xxHash)
  • CSV 输出格式

项目链接

欢迎大家试用并提出建议!如果觉得有用,请给个 ⭐ Star!


🙏 参考资料


关注我,了解更多 Rust 项目实践!

本文首发于 CSDN,转载请注明出处。


标签 : Rust CLI 重复文件 文件管理 开源项目

相关推荐
liu****39 分钟前
10.指针详解(六)
c语言·开发语言·数据结构·c++·算法
Source.Liu40 分钟前
【Chrono库】 时区转换规则(TransitionRule)实现详解(src/offset/local/tz_info/rule.rs)
rust·time
报错小能手42 分钟前
C++流类库 标准输入流的安全性与成员函数 ostream 成员函数与自定义类型的IO
开发语言·c++·cocoa
VBA633742 分钟前
数组与字典解决方案第三十二讲:数组的拆分和维数转换
开发语言
进击的荆棘43 分钟前
C++起始之路——基础知识
开发语言·c++
FAREWELL0007543 分钟前
Lua学习记录(6) --- Lua中的元表相关内容
开发语言·学习·lua
郝学胜-神的一滴44 分钟前
OpenGL错误检查与封装:构建健壮的图形渲染系统
开发语言·c++·程序人生·软件工程·图形渲染
Victor3561 小时前
Redis(160)Redis的安全问题如何解决?
后端
Victor3561 小时前
Redis(161)如何使用Redis实现分布式锁?
后端