#disk-space #deduplication #dupe #file-content #dedupe #deduplicate #file-path

bin+lib dupe-krill

快速文件去重器。将具有相同内容的重复文件替换为硬链接。

8 个稳定版本

1.4.9 2023 年 9 月 4 日
1.4.7 2022 年 1 月 25 日
1.4.6 2021 年 7 月 14 日
1.4.5 2020 年 4 月 10 日
1.4.0 2018 年 8 月 29 日

#89文件系统


duplicate-kriller 中使用

MIT 许可证

43KB
848

Dupe krill — 快速文件去重器

用硬链接替换具有相同内容的文件,以确保所有副本的文件数据只存储一次,节省磁盘空间。适用于减小多个备份、杂乱的图片和音乐收藏、无数的 node_modules 复制、macOS 应用程序包以及其他通常是不可变的文件的大小(因为当任何一个副本被更改时,所有硬链接副本都会更改)。

功能

  • 它非常快,并且内存效率合理。
  • 一旦发现重复项,就增量式去重。
  • 原子性地替换文件,并且可以在任何时候安全中断。
  • 已被证明是可靠的。多年来一直无问题使用。
  • 它了解现有的硬链接,并支持多个硬链接组的合并。
  • 优雅地处理符号链接和特殊文件。

用法

从发布页面下载二进制文件.

在 macOS 和 Linux 上运行。不支持 Windows。

如果您有最新的稳定 Rust(1.42+),可以使用以下任一命令构建程序: cargo install dupe-krill 或克隆此仓库并 cargo build --release

dupe-krill -d <files or directories> # find dupes without doing anything
dupe-krill <files or directories> # find and replace with hardlinks

有关详细信息,请参阅 dupe-krill -h

输出

它每行打印一个重复项。它打印同一行上的两个路径,并将它们之间的差异突出显示为 {first => second}

进度显示

<唯一文件体数量>+<硬链接数量>重复文件。 <检查的文件>+<跳过的文件>已扫描的文件。

符号链接、特殊设备文件和0大小文件始终会被跳过。

不要尝试解析程序通常的输出。如果您想获得机器可读的输出,请添加--json选项。您还可以将此程序用作Rust库以实现无缝集成。

硬链接是如何工作的?

通过创建硬链接来消除文件重复。它们不会被删除。相反,实际上相同的文件将同时在两个或更多目录中存在。与符号链接不同,硬链接表现得像真实文件。删除一个硬链接不会改变其他硬链接。编辑硬链接文件会同时编辑所有位置(除非某些应用程序删除并创建一个新的文件,而不是覆盖现有文件)。硬链接会使文件的副本具有相同的文件权限。

此程序只会消除大于单个磁盘块(通常是4KB)的文件重复,因为在许多文件系统中,硬链接小型文件实际上可能不会节省空间。您也可以添加--s标志来消除小文件的重复。

深入了解快速去重算法

简而言之:它使用Rust标准库的BTreeMap进行去重,但使用一种扭曲,使其能够懒加载地比较文件,只读取必要的最少文件内容。


理论上,您可以通过将它们放入一个巨大的哈希表(汇总文件路径并使用文件内容作为键)来找到所有重复的文件

HashMap<Vec<u8>, Vec<Path>>

但当然,这将需要难以置信的大量内存。您可以通过使用内容哈希而不是内容本身来解决这个问题。

顺便说一句,我无法强调偶然的加密哈希冲突有多不可能。这不仅仅是“如果你幸运的话,你可能会安全”。这是“创建这么多文件所需的能量比我们的文明在其整个历史上产生的能量还要多”。

HashMap<[u8; 16], Vec<Path>>

但这仍然很慢,因为您仍然需要读取所有文件的整个内容。您可以通过首先比较文件大小来节省一些工作

HashMap<u64, HashMap<[u8; 20], Vec<Path>>

但这只能起到一点点作用,因为具有相同大小的文件非常常见。您可以通过首先比较文件的开头来消除更多近重复文件

HashMap<u64, HashMap<[u8; 20], HashMap<[u8; 20], Vec<Path>>>

然后可能只比较文件的结尾,然后可能是中间的一些片段,等等。

HashMap<u64, HashMap<[u8; 20], HashMap<[u8; 20], HashMap<[u8; 20], Vec<Path>>>>
HashMap<u64, HashMap<[u8; 20], HashMap<[u8; 20], HashMap<[u8; 20], HashMap<[u8; 20], HashMap<[u8; 20], >>>>

这些无限嵌套的哈希表可以进行泛化。BTreeMap不需要一次性看到整个键。它只比较键与键,比较可以增量进行——只读取足够的文件内容来显示其键是唯一的,甚至不知道完整的键。

BTreeMap<LazilyHashing<File>, Vec<Path>>

这正是此程序所做的(以及一些inode的处理)。

去重的全部繁重工作由Rust标准库的BTreeMap和重载的</>运算符来完成,这些运算符会增量哈希文件(是的,执行文件I/O的操作符重载是一个绝妙的主意。不幸的是,我无法使用<<)。

依赖项

~3–11MB
~121K SLoC