44 个版本 (24 个破坏性更新)

0.34.0 2023年10月29日
0.32.2 2023年10月18日
0.32.1 2023年6月11日
0.30.0 2023年3月12日
0.12.3 2021年6月20日

#204文件系统

Download history 77/week @ 2024-03-29 47/week @ 2024-04-05 37/week @ 2024-04-12 40/week @ 2024-04-19 61/week @ 2024-04-26 60/week @ 2024-05-03 34/week @ 2024-05-10 58/week @ 2024-05-17 51/week @ 2024-05-24 58/week @ 2024-05-31 49/week @ 2024-06-07 40/week @ 2024-06-14 40/week @ 2024-06-21 191/week @ 2024-06-28 37/week @ 2024-07-05 19/week @ 2024-07-12

每月下载量292次
用于 fclones-gui

MIT 许可证

460KB
10K SLoC

fclones

高效的重复文件查找和删除工具

CircleCI crates.io Documentation License: MIT

这是命令行 fclones 及其核心库的仓库。关于桌面前端,请参阅 fclones-gui


fclones 是一个命令行工具,用于识别相同文件组并删除不再需要的文件副本。它提供了丰富的配置选项来控制搜索范围,并提供多种删除重复文件的方法。为了最大的灵活性,它很好地集成了其他 Unix 工具,如 find,并且支持 JSON,因此您可以对搜索和清理过程有更多的控制。

fclones 对您的数据非常认真。在删除之前,您可以检查和修改重复文件的列表。还有一个 --dry-run 选项可以告诉您文件系统上将要进行哪些更改。

fclones 使用 Rust 实现,在现代化硬件上具有高性能。它采用了许多其他程序中不存在的一些优化技术。它适应硬盘类型,按照 HDD 上的物理数据放置顺序执行文件操作,并行扫描目录树,并在处理数百万文件时使用路径的前缀压缩以减少内存消耗。它还友好地处理页面缓存,不会将数据推离缓存。因此,fclones 在 SSD 或 HDD 存储上比许多其他流行的重复文件查找器表现得更好。

fclones 可用于多种操作系统,但在 Linux 上表现最佳。

功能

  • 识别相同文件组
    • 查找重复文件
    • 查找超过 N 个副本的文件
    • 查找唯一文件
    • 查找少于 N 个副本的文件
  • 高级文件选择,以减少处理的数据量
    • 扫描多个目录根
    • 可以与从标准输入直接管道传输的文件列表一起工作
    • 递归/非递归文件选择
    • 递归深度限制
    • 通过扩展UNIX globs过滤名称和路径
    • 通过正则表达式过滤名称和路径
    • 根据最小/最大文件大小过滤
    • 正确处理符号链接和硬链接
  • 移除冗余数据
    • 移除、移动或用软或硬链接替换文件
    • 使用某些文件系统上的本地copy-on-write(reflink)支持移除冗余的文件数据
    • 根据路径或名称模式选择要删除的文件
    • 根据创建时间、修改时间、最后访问时间或嵌套级别优先删除文件
  • 高性能
    • 在所有I/O和CPU密集型阶段具有并行处理能力
    • 根据设备类型(SSD vs HDD)自动调整并行性和访问策略
    • 由于高度优化的路径表示,内存占用低
    • 提供多种快速非加密和加密哈希函数,宽达512位
    • 不会将数据推离页面缓存(仅限Linux)
    • 可选的文件哈希持久缓存
    • 精确的进度报告
  • 多种输出格式,便于进一步处理结果
    • 标准文本格式
      • 使用分组标题分隔的组,包含文件大小和哈希
      • 每组一行路径
    • 可选的fdupes兼容性(无标题,无缩进,组之间由空白行分隔)
    • 机器可读格式:CSVJSON

限制

Windows不支持copy-on-write文件数据去重(reflink)。

某些优化在其他平台(除Linux外)不可用

  • 根据物理位置对文件访问进行排序
  • 页面缓存滞后

演示

让我们首先创建一些文件

$ mkdir test
$ cd test
$ echo foo >foo1.txt
$ echo foo >foo2.txt
$ echo foo >foo3.txt
$ echo bar >bar1.txt
$ echo bar >bar2.txt

现在让我们识别重复项

$ fclones group . >dupes.txt
[2021-06-05 18:21:33.358] fclones:  info: Started grouping
[2021-06-05 18:21:33.738] fclones:  info: Scanned 7 file entries
[2021-06-05 18:21:33.738] fclones:  info: Found 5 (20 B) files matching selection criteria
[2021-06-05 18:21:33.738] fclones:  info: Found 4 (16 B) candidates after grouping by size
[2021-06-05 18:21:33.738] fclones:  info: Found 4 (16 B) candidates after grouping by paths and file identifiers
[2021-06-05 18:21:33.739] fclones:  info: Found 3 (12 B) candidates after grouping by prefix
[2021-06-05 18:21:33.740] fclones:  info: Found 3 (12 B) candidates after grouping by suffix
[2021-06-05 18:21:33.741] fclones:  info: Found 3 (12 B) redundant files

$ cat dupes.txt
# Report by fclones 0.12.0
# Timestamp: 2021-06-05 18:21:33.741 +0200
# Command: fclones group .
# Found 2 file groups
# 12 B (12 B) in 3 redundant files can be removed
7d6ebf613bf94dfd976d169ff6ae02c3, 4 B (4 B) * 2:
    /tmp/test/bar1.txt
    /tmp/test/bar2.txt
6109f093b3fd5eb1060989c990d1226f, 4 B (4 B) * 3:
    /tmp/test/foo1.txt
    /tmp/test/foo2.txt
    /tmp/test/foo3.txt

最后,我们可以用软链接替换重复项

$ fclones link --soft <dupes.txt 
[2021-06-05 18:25:42.488] fclones:  info: Started deduplicating
[2021-06-05 18:25:42.493] fclones:  info: Processed 3 files and reclaimed 12 B space

$ ls -l
total 12
-rw-rw-r-- 1 pkolaczk pkolaczk   4 cze  5 18:19 bar1.txt
lrwxrwxrwx 1 pkolaczk pkolaczk  18 cze  5 18:25 bar2.txt -> /tmp/test/bar1.txt
-rw-rw-r-- 1 pkolaczk pkolaczk 382 cze  5 18:21 dupes.txt
-rw-rw-r-- 1 pkolaczk pkolaczk   4 cze  5 18:19 foo1.txt
lrwxrwxrwx 1 pkolaczk pkolaczk  18 cze  5 18:25 foo2.txt -> /tmp/test/foo1.txt
lrwxrwxrwx 1 pkolaczk pkolaczk  18 cze  5 18:25 foo3.txt -> /tmp/test/foo1.txt

安装

代码已在Ubuntu Linux 21.10上彻底测试。Windows或Mac OS X等其他系统可能也能运行。欢迎测试和/或将代码移植到其他平台。请报告成功和失败的情况。

官方包

Snap商店(Linux)

snap install fclones

Homebrew(macOS和Linux)

brew install fclones

某些平台的安装包和二进制文件直接附加到发布

第三方包

从源码构建

安装Rust工具链然后运行

cargo install fclones

构建将二进制文件写入.cargo/bin/fclones

使用方法

fclones提供查找和删除文件的不同命令。这样,您可以在对文件系统进行任何修改之前检查找到的文件列表。

  • group - 识别相同的文件组并将它们打印到标准输出
  • remove - 删除先前由group识别的冗余文件
  • link - 用链接替换冗余文件(默认:硬链接)
  • dedupe - 不删除任何文件,但通过使用文件系统的本地copy-on-write功能(reflink)来去重文件数据

查找文件

在当前目录及其子目录中查找重复、唯一、复制不足或复制过多的文件

fclones group .
fclones group . --unique 
fclones group . --rf-under 3
fclones group . --rf-over 3

您可以在多个目录中进行搜索

fclones group dir1 dir2 dir3

默认情况下,隐藏文件和与.gitignore.fdignore中列出的模式匹配的文件将被忽略。要搜索所有文件,请使用

fclones group --no-ignore --hidden dir

限制递归深度

fclones group . --depth 1   # scan only files in the current dir, skip subdirs
fclones group * --depth 0   # similar as above in shells that expand `*` 

注意:0.10版本以下的版本默认不会进入目录。在这些旧版本中,添加-R标志以启用递归目录遍历。

找到匹配两个目录树中的文件,而不匹配每个树中的相同文件

fclones group --isolate dir1 dir2

找到至少100 MB大小的重复文件

fclones group . -s 100M

按文件名或路径模式过滤

fclones group . --name '*.jpg' '*.png' 

find选定的文件上运行fclones(注意:这可能比内置过滤慢)

find . -name '*.c' | fclones group --stdin --depth 0

跟随符号链接,但不逃离主目录

fclones group . -L --path '/home/**'

从扫描中排除目录树的一部分

fclones group / --exclude '/dev/**' '/proc/**'

删除文件

要删除重复文件,需要将它们移动到不同的位置或用链接替换,你需要将fclones group生成的报告发送到fclones removefclones movefclones link命令的标准输入。报告格式将自动检测。目前支持defaultjson报告格式。

假设重复文件列表已保存在文件dupes.txt中,以下命令将删除冗余文件

fclones link <dupes.txt             # replace with hard links
fclones link -s <dupes.txt          # replace with symbolic links
fclones move target_dir <dupes.txt  # move to target_dir  
fclones remove <dupes.txt           # remove totally

如果您希望一次完成所有操作而不将组列表存储在文件中,可以使用管道

fclones group . | fclones link

要选择要保留的文件数量,请使用-n/--rf-over选项。默认情况下,它设置为运行group时使用的值(如果没有明确设置,则为1)。要为每个组留下2个副本,请运行

fclones remove -n 2 <dupes.txt

默认情况下,fclones遵循输入文件中指定的文件顺序。它保留每个列表开头的文件,并删除/替换每个列表末尾的文件。可以通过--priority选项更改此顺序,例如

fclones remove --priority newest <dupes.txt        # remove the newest replicas
fclones remove --priority oldest <dupes.txt        # remove the oldest replicas

有关更多优先级选项,请参阅fclones remove --help

还可以将删除文件的限制仅限于与模式匹配的文件名或路径

fclones remove --name '*.jpg' <dupes.txt       # remove only jpg files
fclones remove --path '/trash/**' <dupes.txt   # remove only files in the /trash folder

如果指定不想删除的文件的模式更容易,则可以使用keep选项之一

fclones remove --keep-name '*.mov' <dupes.txt           # never remove mov files
fclones remove --keep-path '/important/**' <dupes.txt   # never remove files in the /important folder

为了确保您不会意外删除错误的文件,请使用--dry-run选项。此选项将打印出将要执行的所有命令,但实际上不会执行它们

fclones link --soft <dupes.txt --dry-run 2>/dev/null

mv /tmp/test/bar2.txt /tmp/test/bar2.txt.jkXswbsDxhqItPeOfCXsWN4d
ln -s /tmp/test/bar1.txt /tmp/test/bar2.txt
rm /tmp/test/bar2.txt.jkXswbsDxhqItPeOfCXsWN4d
mv /tmp/test/foo2.txt /tmp/test/foo2.txt.ze1hvhNjfre618TkRGUxJNzx
ln -s /tmp/test/foo1.txt /tmp/test/foo2.txt
rm /tmp/test/foo2.txt.ze1hvhNjfre618TkRGUxJNzx
mv /tmp/test/foo3.txt /tmp/test/foo3.txt.ttLAWO6YckczL1LXEsHfcEau
ln -s /tmp/test/foo1.txt /tmp/test/foo3.txt
rm /tmp/test/foo3.txt.ttLAWO6YckczL1LXEsHfcEau

由符号链接或硬链接连接的文件不视为重复文件。您可以通过设置以下标志来更改此行为

  • 当设置--isolate
    • 位于不同目录树中的链接被视为重复项
    • 位于同一目录树中的链接计为一个副本。
  • 当设置--match-links时,fclones将所有链接文件视为重复文件。

考虑以下目录结构,其中所有文件都是共享相同内容的硬链接

dir1:
  - file1
  - file2
dir2:
  - file3
  - file4

因为所有文件本质上是相同的数据,它们最终将出现在同一个文件组中,但该文件组中实际存在的副本数量将根据提供的标志而有所不同

命令 副本数量 报告的组 要删除的文件
fclones group dir1 dir2 1
fclones group dir1 dir2--isolate 2 file3, file4
fclones group dir1 dir2--match-links 4 file2, file3, file4

group 命令默认忽略文件符号链接,除非设置了至少 --follow-links--symbolic-links 标志。如果仅设置了 --follow-links,则会跟随文件符号链接并解析到它们的目标。如果设置了 --symbolic-links,则不会跟随文件符号链接,但会将其视为硬链接并在输出报告中可能报告。当同时设置了 --symbolic-links--follow-links 时,会跟随目录符号链接,但文件符号链接会被视为硬链接。

注意:将 --match-links--symbolic-links 一起使用非常危险。很容易导致删除唯一的常规文件,并留下大量孤儿符号链接。

预处理器文件

使用 --transform 选项通过外部命令安全地转换文件。默认情况下,转换发生在文件数据的副本上,以避免意外数据丢失。请注意,此选项可能会显著减慢大量文件的处理速度,因为它为每个文件调用外部程序。

以下命令在匹配重复的 jpg 图像之前会去除 exif 信息

fclones group . --name '*.jpg' -i --transform 'exiv2 -d a $IN' --in-place     

其他

列出更多选项

fclones [command] -h      # short help
fclones [command] --help  # detailed help

路径通配符

fclones 理解 Bash 扩展通配符的一个子集。可以使用以下通配符:

  • ? 匹配除目录分隔符以外的任何字符
  • [a-z] 匹配方括号中给出的字符或字符范围之一
  • [!a-z] 匹配方括号中未给出的任何字符
  • * 匹配除目录分隔符以外的任何字符序列
  • ** 匹配包括目录分隔符在内的任何字符序列
  • {a,b} 匹配花括号内逗号分隔的任意一个模式
  • @(a|b){a,b} 相同
  • ?(a|b) 匹配方括号内模式的最多一次出现
  • +(a|b) 匹配方括号内给定模式的至少一次出现
  • *(a|b) 匹配方括号内给定模式的任意次数出现
  • \ 在 Unix-like 系统上转义通配符,例如 \? 会匹配 ? 的字面意思
  • ^ 在 Windows 上转义通配符,例如 ^? 会匹配 ? 的字面意思

注意

  • 在类Unix系统中,使用通配符时必须非常小心,以避免shell意外地扩展通配符。在许多情况下,您可能不希望shell而不是fclones来扩展通配符。在这种情况下,您需要引用通配符。

    fclones group . --name '*.jpg'       
    
  • 在Windows上,默认的shell在将参数传递给程序之前不会移除引号,因此您需要不引用地传递通配符。

    fclones group . --name *.jpg
    
  • 在Windows上,默认的shell不支持路径通配符,因此路径中使用的通配符*和?将被原样传递,并且很可能创建无效的路径。例如,以下在Bash中在当前目录中搜索重复文件的命令,在默认的Windows shell中可能会失败:

    fclones group *
    

    如果您需要路径通配符,而您的shell不支持它,请使用--name--path提供的内置路径通配符。

算法

文件在几个阶段进行处理。除了最后一个阶段之外,每个阶段都是并行的,但必须完全完成上一个阶段才能开始下一个阶段。

  1. 扫描输入文件并过滤与选择标准匹配的文件。如果请求,递归地遍历目录。如果请求,则跟随符号链接。对于与选择标准匹配的文件,读取它们的尺寸。
  2. 通过将它们存储在哈希映射中按尺寸分组收集的文件。删除小于所需下限(默认为2)的小组。
  3. 在每组中,删除具有相同inode id的重复文件。当存在硬链接时,相同文件可能通过不同的路径访问。此步骤可以可选地跳过。
  4. 对于每个剩余的文件,计算初始数据小块的哈希。将具有不同哈希的文件放入不同的组中。如有必要,修剪结果组。
  5. 对于每个剩余的文件,计算文件末尾数据小块的哈希。将具有不同哈希的文件放入不同的组中。如有必要,修剪小组。
  6. 对于每个剩余的文件,计算文件全部内容的哈希。请注意,对于小文件,我们可能在步骤4中已经计算了完整的文件内容哈希,因此可以安全地忽略这些文件。与步骤4和5相同,分割组并移除那些过小的组。
  7. 将报告写入stdout。

请注意,没有进行逐字节比较文件。所有可用的哈希函数至少为128位宽,您不需要担心哈希冲突。在1015个文件中,使用128位哈希时,冲突的概率为0.000000001,不考虑文件还需要匹配尺寸的要求。

哈希

您可以使用--hash-fn(默认:metro)选择哈希函数。非加密哈希比加密哈希效率高得多,但是除非您从快速的SSD读取或文件数据已缓存,否则您可能不会看到太大的差异。

哈希函数 哈希宽度 加密
metro 128位
xxhash3 128位
blake3 256位
sha256 256位
sha512 512位
sha3-256 256位
sha3-512 512位

调整

本节提供了关于如何从fclones中获得最佳性能的提示。

增量模式

如果您预计将在同一组文件上多次运行fclones group,您可能从启用哈希缓存中受益,通过添加--cache标志。

fclones group --cache <dir>

缓存可以在后续运行fclones时显著提高分组速度,但需要额外的存储空间来存储缓存。缓存还允许在中断后快速恢复工作,因此如果您计划在大型数据集上运行fclones,则建议使用它。

缓存的工作方式如下:

  • 每个新计算的文件哈希都会持久化在缓存中,同时还有一些文件元数据,例如修改时间戳和长度。
  • 每当需要计算文件哈希时,首先会在缓存中查找。如果当前文件的元数据与缓存中存储的元数据严格匹配,则会使用缓存的哈希值。

由于文件是通过其内部标识符(如Unix上的inode标识符)来识别的,而不是通过路径名,且移动/重命名通常保留这些标识符,因此缓存的哈希值不会被文件移动操作失效。

请注意,缓存依赖于文件元数据来检测文件内容的更改。如果文件在修改时没有立即更新文件修改时间戳和文件长度,这可能会在分组过程中引入一些不准确之处。大多数文件系统在关闭文件时自动更新时间戳。因此,长时间保持打开状态(例如,由数据库系统保持打开)的已更改文件可能不会被 fclones group 发现,并可能使用过时的缓存值。

缓存数据库位于用户账户的标准缓存目录中。通常,这些是

  • Linux: $HOME/.cache/fclones
  • macOS: $HOME/Library/Caches/fclones
  • Windows: $HOME/AppData/Local/fclones

配置并行性

--threads 参数控制内部线程池的大小。当您不希望 fclones 过度影响系统性能时,可以使用此参数来降低并行级别,例如,当您需要同时执行其他工作时。如果您需要减少内存使用,我们建议降低并行级别。

当使用 fclones 的版本 0.6.x 或更高版本,对每个文件至少有几种MB大小的文件进行去重时
在旋转磁盘中(HDD),建议将 --threads 1 设置,因为在HDD上从多个线程访问大文件可能比单线程访问慢得多(YMMV,这高度依赖于操作系统,已报道的性能差异为2x-10x)。

从版本 0.7.0 开始,fclones 使用针对最终哈希的独立设备线程池,并根据设备类型自动调整并行级别、内存缓冲区大小和部分哈希大小。这些自动设置可以通过 -threads 覆盖。

以下选项可以传递给 --threads。更具体的选项会覆盖不具体的选项。

  • main:<n> – 设置用于随机I/O(目录树扫描、文件元数据检索和内存排序/哈希)的主线程池的大小。这些操作通常从高并行级别中受益,即使在旋转磁盘上也是如此。默认情况下未设置,这意味着池将配置为使用所有可用的CPU核心。
  • dev:<device>:<r>,<s> – 设置在具有给定名称的块设备上用于随机I/O的线程池 r 和用于顺序I/O的线程池 s 的大小。设备名称依赖于操作系统。请注意,这不同于分区名称或挂载点。
  • ssd:<r>,<s> – 设置用于固态硬盘I/O的线程池大小。默认不设置。
  • hdd:<r>,<s> – 设置用于旋转硬盘I/O的线程池大小。默认为 8,1
  • removable:<r>,<s> – 设置用于可移动设备(例如USB闪存盘)I/O的线程池大小。默认为 4,1
  • unknown:<r>,<s> – 设置用于未知类型设备I/O的线程池大小。有时设备类型无法确定,例如如果它被挂载为NAS。默认为 4,1
  • default:<r>,<s> – 设置所有未设置选项使用的池大小
  • <r>,<s> - 与 default:<r>,<s>
  • <n> - 与 default:<n>,<n>

示例

将主线程池的并行级别限制为1

fclones group <paths> --threads main:1  

限制所有SSD设备的所有I/O访问的并行级别

fclones group <paths> --threads ssd:1 

将随机I/O访问的并行级别设置为核心数,将顺序I/O访问的并行级别设置为2,针对/dev/sda块设备

fclones group <paths> --threads dev:/dev/sda:0,2 

可以给出多个 --threads 选项,用空格分隔

fclones group <paths> --threads main:16 ssd:4 hdd:1,1     

基准测试

不同的重复查找器被分配了在大量文件集中查找重复的任务。在每次运行之前,使用 echo 3 > /proc/sys/vm/drop_caches 清除系统页面缓存。

SSD基准测试

  • 型号:Dell Precision 5520
  • CPU:Intel(R) Xeon(R) CPU E3-1505M v6 @ 3.00GHz
  • RAM:32 GB
  • 存储:本地NVMe SSD 512 GB
  • 系统:Ubuntu Linux 20.10,内核5.8.0-53-generic
  • 任务:1,460,720路径,316 GB数据
程序 版本 语言 时间 峰值内存
fclones 0.12.1 Rust 0:34.59 266 MB
yadf 0.15.2 Rust 0:59.32 329 MB
czkawka 3.1.0 Rust 2:09.00 1.4 GB
rm lint 2.9.0 C, Python 2:28.43 942 MB
jdupes 1.18.2 C 5:01.91 332 MB
dupe-krill 1.4.5 Rust 5:09.52 706 MB
fdupes 2.1.1 C 5:46.19 342 MB
rdfind 1.4.1 C++ 5:53.07 496 MB
dupeguru 4.1.1 Python 7:49.89 1.4 GB
fdupes-java 1.3.1 Java >> 20 minutes 4.2 GB

fdupes-java 测试未完成。在它仍在第二/第三阶段计算 MD5 的 20 分钟后,我中断了它。不幸的是 fdupes-java 不显示有用的进度条,因此无法估计它需要多长时间。

HDD 基准测试

  • 型号:Dell Precision M4600
  • CPU:Intel(R) Core(TM) i7-2760QM CPU @ 2.40GHz
  • RAM:24 GB
  • 系统:Mint Linux 19.3,内核 5.4.0-70-generic
  • 存储:Seagate Momentus 7200 RPM SATA 硬盘,EXT4 文件系统
  • 任务:51370 个路径,2 GB 数据,6811 个(471 MB)重复文件

使用的命令

  /usr/bin/time -v fclones -R <file set root> 
  /usr/bin/time -v jdupes -R -Q <file set root>
  /usr/bin/time -v fdupes -R <file set root>
  /usr/bin/time -v rdfind <file set root>

在这个基准测试中,在每次运行之前都删除了页面缓存。

程序 版本 语言 线程 时间 峰值内存
fclones 0.9.1 Rust 1 0:19.45 18.1 MB
rdfind 1.3.5 C++ 1 0:33.70 18.5 MB
yadf 0.14.1 Rust 1:11.69 22.9 MB
jdupes 1.9 C 1 1:18.47 15.7 MB
fdupes 1.6.1 C 1 1:33.71 15.9 MB

依赖项

~20–33MB
~507K SLoC