用 fdupes 将重复文件替换为硬链接并核算空间收益
【摘要】 介绍在 Linux 上用 fdupes 递归找出重复文件,先按大小缩小候选,再比对内容并把多余副本替换为硬链接;脚本校验 inode 与链接数,并按旧 inode 的分配块数统计实际节省空间。
用 fdupes 找出重复文件并把多余副本替换为硬链接
场景
在数据盘、构建缓存或归档目录中,常见内容完全相同但路径不同的文件。直接删除多余副本有风险,保留又多占空间。若这些文件位于同一文件系统,且后续不需要独立修改,可以把多余副本替换为指向保留文件的硬链接:目录结构不变,重复数据只保留一份。
本文用 fdupes 找出重复组,用脚本逐个替换硬链接,并校验 inode 与链接数。空间统计按旧副本 inode 实际分配的 512 字节块计算,而不是简单累加文件逻辑大小。
思路
fdupes -r DIR 会递归扫描目录。它先按文件大小缩小候选范围,再对同大小文件比对内容,因此适合做重复文件发现。脚本读取 fdupes 输出的重复组,每组保留第一个文件,其余文件用 ln -f 指向保留文件。链接前用 cmp -s 再做一次内容复核,并检查设备号,避免跨文件系统硬链接失败。链接后检查两个路径的 inode 是否一致、链接数是否至少为 2。
注意:硬链接共享 inode,权限、属主、时间戳等元数据也会跟随保留文件;如果原副本权限不同,替换后会丢失差异。只建议用于归档、只读缓存或可接受统一元数据的目录。
最小脚本
#!/usr/bin/env bash
set -euo pipefail
root=${1:?用法: $0 DIR}
list=$(mktemp)
trap 'rm -f "$list"' EXIT
# 只列出重复组,不自动删除;fdupes 内部按大小预筛选并比对内容
fdupes -r -- "$root" > "$list"
saved=0
while IFS=$'\t' read -r keep dup; do
[[ -f "$keep" && -f "$dup" ]] || continue
[[ "$keep" != "$dup" ]] || continue
keep_dev=$(stat -c %d -- "$keep")
dup_dev=$(stat -c %d -- "$dup")
if [[ "$keep_dev" != "$dup_dev" ]]; then
printf '跳过跨文件系统: %s -> %s\n' "$dup" "$keep"
continue
fi
keep_ino=$(stat -c %i -- "$keep")
dup_ino=$(stat -c %i -- "$dup")
[[ "$keep_ino" == "$dup_ino" ]] && continue
if ! cmp -s -- "$keep" "$dup"; then
printf '内容不一致,跳过: %s -> %s\n' "$dup" "$keep"
continue
fi
old_links=$(stat -c %h -- "$dup")
old_blocks=$(stat -c %b -- "$dup") # 512 字节块
ln -f -- "$keep" "$dup"
new_keep_ino=$(stat -c %i -- "$keep")
new_dup_ino=$(stat -c %i -- "$dup")
new_links=$(stat -c %h -- "$dup")
if [[ "$new_keep_ino" != "$new_dup_ino" || "$new_links" -lt 2 ]]; then
printf '校验失败: %s -> %s\n' "$dup" "$keep" >&2
exit 1
fi
if [[ "$old_links" -eq 1 ]]; then
saved=$((saved + old_blocks * 512))
fi
printf '已链接: %s -> %s (inode=%s, links=%s)\n' \
"$dup" "$keep" "$new_dup_ino" "$new_links"
done < <(awk 'BEGIN{RS="";FS="\n"} NF>1 {for(i=2;i<=NF;i++) print $1 "\t" $i}' "$list")
printf '按 512 字节块估算,本次实际释放: %d 字节\n' "$saved"
运行方法
- 先在测试目录准备若干重复文件,并保留备份。
- 保存脚本为
dedupe-hardlink.sh,执行chmod +x dedupe-hardlink.sh。 - 用占位目录运行:
./dedupe-hardlink.sh /data/placeholder。 - 查看输出中的 inode、links 和释放字节数;再用
stat抽查:
stat -c '%i %h %s %n' /data/placeholder/a.bin /data/placeholder/b.bin
两个路径应显示相同 inode,链接数至少为 2。
边界与限制
- 硬链接只能在同一文件系统内创建;脚本会跳过设备号不同的组合。
- 只处理普通文件;目录、符号链接、特殊文件不应按此方式替换。
- 文件名含换行符时,基于按行解析的示例不适用;生产环境应使用 NUL 分隔并确认 fdupes 版本支持。
- 旧副本 inode 若还有其它硬链接,替换当前路径不会立即释放数据块,脚本只在旧链接数为 1 时计入节省。
- 运行前先备份;替换会改变元数据关联,且后续对任一链接的写入会影响所有链接。
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)