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

## 📌 前言

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

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

> **项目地址**：
> - GitHub: https://github.com/Waitfish/dupfinder
> - crates.io: https://crates.io/crates/dupfinder

---

## 🎯 项目背景

### 为什么要写这个工具？

市面上已经有一些重复文件查找工具，比如：
- **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 页面](https://github.com/Waitfish/dupfinder/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 输出格式

### 项目链接

- **GitHub**: https://github.com/Waitfish/dupfinder
- **crates.io**: https://crates.io/crates/dupfinder
- **安装**: `cargo install dupfinder`

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

---

## 🙏 参考资料

- [Rust 官方文档](https://doc.rust-lang.org/)
- [Cargo Book](https://doc.rust-lang.org/cargo/)
- [fdupes](https://github.com/adrianlopezroche/fdupes)
- [rdfind](https://github.com/pauldreik/rdfind)

---

**关注我，了解更多 Rust 项目实践！**

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

---

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

