jameswintermute/hasher
GitHub: jameswintermute/hasher
一款纯 Shell 实现的 NAS 文件完整性监控与去重工具,通过 SHA-256 哈希快照检测静默损坏、追踪文件变更并安全清理重复文件。
Stars: 0 | Forks: 0
# Hasher — NAS 文件哈希工具、完整性监控与重复文件查找器
一个纯 shell 工具,通过 SHA-256 哈希为 NAS 上的每个文件建立目录。因此,你可以安全地查找重复文件、 reclaim space safely,并随着时间的推移,证明哪些文件发生了更改、消失或静默损坏。兼容 Synology DSM / BusyBox,除了标准的 Unix 工具外没有任何依赖。
## Hasher 存在的意义
大型文件集合会悄无声息地衰退。几十年来积累的照片和文档在备份和导入过程中会产生重复文件;磁盘上的数据位会在毫无预兆的情况下发生腐化(bit-rot);文件可能会因为意外删除、磁盘故障、同步错误或恶意行为而消失,而且直到需要该文件时才发现它已经不见了。SHA-256 哈希是文件确切内容的指纹。如果你定期为每个文件生成指纹,你就掌握了一份精确的记录,记载了在每个时间点存在哪些文件以及每个文件的内容。这份记录是解答关乎长期数据完整性问题的关键基础:
- **静默损坏** — 文件名和时间戳保持不变,但文件内容是否发生了变化?(这是 bit-rot 和篡改的典型特征。)
- **静默或恶意删除** — 哪些文件上个月还在,现在却不见了,且没有任何故意的操作可以解释?
- **变更追踪** — 自上次运行以来,添加、修改或移动了哪些内容?
去重是该工具立竿见影的一半功能:它查找相同的文件和完全相同的文件夹,帮助你安全地回收空间。完整性监控则是长期战略的另一半:相同且重复捕获的哈希值将成为审计追踪记录。
## Hasher 是什么
Hasher 是一款**内容完整性工具**。它的唯一工作是可靠地计算文件哈希,并安全地根据这些哈希采取行动:
- 通过 SHA-256 为每个文件建立目录(可选跨 CPU 核心并行执行)
- 检测重复的**文件**(内容相同)和重复的**文件夹**(整个目录树完全相同——最高效的清理方式)
- 安全移除重复文件:隔离优先,行动前规划,移动前重新验证
- 查找并移除零长度文件和 OS 垃圾残留物
- 每次运行都会输出带有时间戳的完整目录 CSV 文件
Hasher 刻意保持了功能的专一性。它计算哈希、查找重复项、安全移除并写入 CSV。它只专注做好一件事,从而保持足够小的体积以便于审计,并且足够稳定,值得信赖其在 NAS 上执行 root 级别的删除操作。
## 完整性监控的工作原理
Hasher 本身不会将这次运行与上次运行进行对比(diff)——这超出了其范围,而正是这种克制使得核心程序保持了小巧。相反,**每次哈希运行都会将带有时间戳的 CSV 写入** `hashes/` 目录(`hasher-YYYY-MM-DD-HHMM.csv`):每个文件一行,记录其路径、大小、修改时间、算法和哈希值。
这些 CSV 是完整性监控的基础。由于每个 CSV 都是目录完整的、带有日期的快照,对比其中两个就能准确揭示发生了什么变化:
- 存在于旧 CSV 但不存在于新 CSV 中的路径已被**删除**。
- 在两者中都存在,具有**相同的大小和 mtime 但哈希值不同**的路径,是**静默损坏或篡改**的指纹。
- 哈希值随 mtime 更新而改变的路径是正常的**编辑**。
- 仅存在于新 CSV 中的路径已被**添加**。
独立的、专门构建的工具会使用这些 CSV 并报告跨迭代的差异。将对比功能保留在独立的项目中,意味着系统中以 root 权限运行并移动文件的部分保持最小化且可审计,而只*读取* CSV 的分析功能可以独立演进。Hasher 的契约仅仅是生成诚实、完整、带时间戳的快照;你通过对比它们所获得的信息都是在此基础之上构建的。
## 快速开始
```
git clone https://github.com/jameswintermute/hasher.git
cd hasher
chmod +x launcher.sh bin/*.sh
./launcher.sh # first launch runs a short guided setup
```
首次启动时,Hasher 会提供一个简短的、可跳过的引导设置:它会检查依赖项,帮助你为硬件选择合适的并行哈希级别,提示输入要扫描的目录,并向你展示隔离区的位置。它配置的所有内容之后也可以在菜单中访问。
## 关于
项目作者:**James Wintermute** — jameswintermute@protonmail.ch
始于 2022 年 12 月。当前版本:**v1.3.2**
完整历史记录请参见:`version-history.md`
## 环境要求
- Synology DSM、macOS 或任何带有 bash 的 Linux 环境
- 标准工具:`bash`、`awk`、`sort`、`stat`、`find`、`mv`、`rm`、`xargs`
- 推荐安装位置:你扫描的 volume 上的任何位置(例如 `/volume1/Tools/hasher`)。隔离区将在工具旁边创建。
跨平台支持已在 Synology DSM、Linux 和 macOS 上经过测试。感知宿主机环境的默认设置(排除项、隔离路径)通过 `lib/host-detect.sh` 自动应用。
## 启动器菜单
```
Stage 1 — Hash
1) Start hashing (NAS-safe defaults)
a) Advanced / custom hashing
s) Hashing status
p) Performance settings (parallel hashing)
Stage 2 — Identify
2) Find duplicate files
3) Find duplicate folders
f) Find file by hash (lookup)
Stage 3 — Review & clean
4) Review duplicate FILES (interactive)
r) Review duplicate FOLDERS plan (interactive)
5) Auto-dedup (keep shortest path — no prompts)
6) Apply dedup plan (FILE or FOLDER)
7) Delete zero-length files
8) Delete junk (uses local/junk-extensions.txt)
9) Clean cache files & @eaDir (safe)
Other
d) System diagnostics (deps & readiness)
l) Follow logs (tail -f background.log)
t) Stats & scheduling hints
v) Clean internal working files (var/)
c) Clean logs (rotate & prune)
q) Quit
```
数字键 1–9 驱动主要工作流程。字母涵盖元数据和不常用的操作:`a`/`s`/`p` 用于哈希变体、状态和性能;`f` 用于哈希查找;`r` 用于文件夹计划审查;`d/l/t/v/c` 用于诊断和日常维护。
## 性能 — 并行哈希
默认情况下,Hasher 串行(单线程)计算文件哈希,与其最初的行为保持一致。在具有 SSD 或 SHR 存储的多核系统上,并行哈希可以大幅减少大型任务的运行时间——因为在庞大的小文件语料库(如照片库)中,主导实际耗时的是单文件进程开销,而不是哈希运算。
通过 **`p` 菜单选项**(性能设置)设置线程数。它会检测你的 CPU 核心,推荐一个安全的值(`min(cores, 4)`),并将你的选择持久化到 `var/jobs.conf` 中。你也可以直接设置:
```
# 一次性:
bin/hasher.sh --pathfile local/paths.txt --jobs 4
# 或者在 local/hasher.conf 中:
[performance]
jobs = 4
```
串行和并行运行产生相同的哈希输出;并行只是改变了行写入到 CSV 的顺序。
## 推荐工作流程
### 对于大型存储卷 — 使用自动去重(选项 5)
当你有成百上千个重复项组并且不需要逐组审查时,选项 5 一步即可处理整个过程:
1. 运行 **option 1** — 计算所有文件的哈希
2. 运行 **option 2** — 查找重复文件
3. 运行 **option 5** — 自动去重(生成计划并提供应用选项)
自动去重会保留每个重复项组中**文件路径最短**的副本,并将其他副本隔离。可配置为保留最长路径、最新或最旧的副本。
### 谨慎审查 — 先处理文件夹,再处理文件
与逐个文件审查相比,文件夹去重每次决策能消除更多的冗余。请先运行它:
1. 运行 **option 1** — 计算所有文件的哈希
2. 运行 **option 3** — 查找重复文件夹
3. 运行 **option r** — 交互式审查文件夹计划;对每个组进行接受、跳过或交换保留项的操作;审查器会生成一份已审查的计划
4. 运行 **option 6** → `d` — 应用已审查的文件夹计划
5. 运行 **option 2** — 查找重复文件(现在数量会少得多)
6. 运行 **option 4** — 交互式审查文件组
7. 运行 **option 6** → `f` — 应用文件计划
当你运行选项 3 时,系统会立即为你提供审查器。如果你想先在另一个终端中检查计划,可以拒绝;随时可以通过选项 `r` 返回继续。
## 计划文件
所有去重操作在移动任何文件之前,都会在 `logs/` 中生成一个纯文本计划文件。检查无误后再应用。
```
# 查看将删除的内容(file dedup):
cat logs/auto-dedup-plan-*.txt | grep '^DEL' | head -50
# 查看 folder dedup 计划:
cat logs/duplicate-folders-plan-*.txt | head -20
# 在交互式审查文件夹之后:
cat logs/duplicate-folders-plan-reviewed-*.txt
```
**文件计划格式**(每行一个决策,带有标记)。从 v1.2.0 开始,`DEL` 行带有预期内容哈希作为第三个字段,以便在隔离前对文件进行重新验证:
```
KEEP|/volume1/James/Photos/IMG_001.jpg
DEL|/volume1/James/Backup/Photos/IMG_001.jpg|3a7bd3e2360a3d29eea436fcfb7e44c7...
DEL|/volume1/James/Archive/Photos/IMG_001.jpg|3a7bd3e2360a3d29eea436fcfb7e44c7...
```
旧版的双字段计划(`DEL|path`,无哈希)仍然被接受——只是会跳过重新验证,并发出警告,退回到存在性检查。
**文件夹计划格式**(每行一个路径;所有列出的路径都将被隔离;隐含的保留项是每个组中*未*列出的那个):
```
/volume1/James/Backup/Photos
/volume1/James/Archive/Photos
```
文件夹去重查找器还会写入一个 `duplicate-folders-groups-*.tsv` 辅助文件,其中包含完整的保留/删除结构以及可回收的空间大小,供审查器使用。所有标记为删除的文件都会被移至**隔离区**,而不是永久删除。
## 配置
```
default/hasher.conf — defaults (do not edit)
local/hasher.conf — your overrides
local/paths.txt — scan roots, one per line
local/excludes.txt — find exclusion patterns
local/exceptions-hashes.txt — hashes excluded from dedup
local/excluded-from-dedup.txt — path prefixes excluded from dedup
local/junk-extensions.txt — rules for junk file cleanup
```
优先级:`CLI flags > local/hasher.conf > default/hasher.conf`
并行哈希优先级:`--jobs flag > hasher.conf [performance] jobs > var/jobs.conf(由 'p' 菜单设置)> HASH_JOBS env > default (1)`。
## 目录结构
```
hasher/
├── bin/
│ ├── apply-folder-plan.sh
│ ├── auto-dedup.sh
│ ├── check-deps.sh
│ ├── clean-logs.sh
│ ├── csv-quick-stats.sh
│ ├── delete-duplicates.sh
│ ├── delete-junk.sh
│ ├── delete-zero-length.sh
│ ├── du-summary.sh
│ ├── find-duplicate-folders.sh
│ ├── find-duplicates.sh
│ ├── hash-check.sh
│ ├── hasher.sh
│ ├── launch-review.sh
│ ├── review-duplicates.sh
│ ├── review-folder-plan.sh ← v1.1.13
│ ├── review-junk.sh
│ └── run-find-duplicates.sh
│
├── lib/
│ └── host-detect.sh ← v1.1.9
│
├── default/
│ └── hasher.conf
│
├── local/ — your config (gitignored)
│ ├── exceptions-hashes.txt
│ ├── excluded-from-dedup.txt
│ ├── excludes.txt
│ ├── hasher.conf
│ ├── junk-extensions.txt
│ └── paths.txt
│
├── logs/ — plan files and reports (gitignored)
├── hashes/ — hash CSVs (gitignored)
├── var/ — working files, jobs.conf (gitignored)
├── quarantine/ — files moved by delete-duplicates.sh
│
├── launcher.sh
├── LICENSE
├── readme.md
└── version-history.md
```
## 安全模型
**去重(核心工作流程)采用隔离优先原则,绝不执行永久删除:**
- 在移动任何文件之前,都会生成计划并供审查
- **内容重新验证 (v1.2.0):**在隔离之前,`delete-duplicates.sh` 会重新计算每个候选项的哈希,如果内容不再与计划中的哈希匹配,则跳过该项——保护在规划和应用期间被修改的文件
- 文件夹去重审查器(选项 `r`)允许你在应用任何操作之前,对每个重复组进行接受、跳过或交换保留项的操作
- 应用原始(未经审查的)文件夹计划时会提示进行明确确认
- `delete-duplicates.sh` 和 `apply-folder-plan.sh` 将文件移至隔离区——而非永久删除;`apply-folder-plan.sh` 使用防冲突的隔离命名方式 (v1.1.6+)
- 异常列表防止重新标记已知的重复项
**维护辅助脚本默认执行删除**——这些与去重是分开的,除非特别说明,否则将永久删除文件:
- `delete-zero-length.sh` 删除空文件;传递 `--quarantine` 参数可改为移动它们
- `delete-junk.sh` 永久移除匹配 `local/junk-extensions.txt` 的文件
- cache/`@eaDir` 清理将永久移除这些缓存
- 所有脚本均支持 `--force`/dry-run 模式;请先审查计划或在未加 `--force` 的情况下试运行
**常规:**
- 所有脚本在执行操作前都会立即重新验证路径
- 已审计 Bash 3.2 / BSD awk / macOS 用户空间兼容性 (v1.1.9–v1.1.12)
## 故障排除
**大小在重复审查中显示为 `??`**
通过 SSH 直接在 NAS 上运行 `review-duplicates.sh`——它无法 stat 远程路径。
**CSV 似乎已损坏**
修复换行符:`sed -i 's/\r$//' hashes/*.csv`
**“All paths missing or unreadable” 错误**
`local/paths.txt` 中的路径在当前主机上不存在。常见原因:外部驱动器未挂载、volume 名称拼写错误、NAS 共享脱机。使用 `ls /Volumes` (macOS)、`ls /mnt` 或 `ls /media` (Linux) 或 `ls /volume1` (Synology) 进行检查。
**文件夹审查提示 “no groups TSV found”**
请先运行选项 3(查找重复文件夹)。审查器会读取由查找器生成的 `logs/duplicate-folders-groups-*.tsv` 文件。
**“Content changed since plan was made — SKIPPING”**
预期且安全的行为:某个文件在计算哈希和应用期间发生了更改,因此它不再是经过验证的重复项。重新运行哈希和去重以重新评估它。
**并行哈希让我的 NAS 变慢了**
你可能在使用单个机械硬盘 (HDD)。通过 `p` 菜单将线程数设置回 1–2。并行有助于 SSD/SHR 阵列,对单盘无效。
## 许可证
GNU GPLv3 — 详见 LICENSE。
## 延伸阅读
- [Facebook — Silent Data Corruption](https://engineering.fb.com/2021/02/23/data-infrastructure/silent-data-corruption/) — 基于哈希的完整性监控的动机用例
标签:Cutter, NAS, SHA-256, Shell脚本, 去重, 完整性校验, 应用安全, 文件存储