vum*_*ume 17 find file-comparison
在处理 jpg 或 h264 压缩文件时,像 fdupes 这样的工具是荒谬的矫枉过正。两个具有完全相同文件大小的此类文件已经很好地表明它们是相同的。
除此之外,如果提取并比较 16 个等距的 16 字节块,并且它们也相同,那么就有足够的证据让我假设它们是相同的。有这样的事吗?
(顺便说一句,我知道文件大小本身可能是一个相当不可靠的指标,因为可以选择压缩到某些目标大小,例如 1MB 或 1 CD/DVD。如果在许多文件上使用相同的目标大小,这是相当合理的一些不同的文件将具有完全相同的大小。)
A.L*_*A.L 12
czkawka是一个开源工具,旨在查找重复文件(以及图像、视频或音乐)并通过命令行或图形界面呈现它们,重点是速度。您可能会对文档中的这一感兴趣:
更快地扫描大量重复项
默认情况下,按相同大小分组的所有文件都会计算部分哈希值(每个文件仅 2KB 的哈希值)。这种哈希值的计算速度通常非常快,尤其是在 SSD 和快速多核处理器上。但是,当使用 HDD 或慢速处理器扫描数十万或数百万个文件时,此步骤通常可能需要很长时间。
在 GUI 版本中,哈希值将存储在缓存中,以便以后搜索重复项会更快。
例子:
创建一些测试文件:
我们生成随机图像,然后复制a.jpg
到b.jpg
以获得副本。
$ convert -size 1000x1000 plasma:fractal a.jpg
$ cp -v a.jpg b.jpg
'a.jpg' -> 'b.jpg'
$ convert -size 1000x1000 plasma:fractal c.jpg
$ convert -size 1000x1000 plasma:fractal d.jpg
$ ls --size
total 1456
364 a.jpg 364 b.jpg 364 c.jpg 364 d.jpg
Run Code Online (Sandbox Code Playgroud)
仅检查尺寸:
$ linux_czkawka_cli dup --directories /run/shm/test/ --search-method size
Found 2 files in 1 groups with same size(may have different content) which took 361.76 KiB:
Size - 361.76 KiB (370442) - 2 files
/run/shm/test/b.jpg
/run/shm/test/a.jpg
Run Code Online (Sandbox Code Playgroud)
通过哈希值检查文件:
$ linux_czkawka_cli dup --directories /run/shm/test/ --search-method hash
Found 2 duplicated files in 1 groups with same content which took 361.76 KiB:
Size - 361.76 KiB (370442) - 2 files
/run/shm/test/b.jpg
/run/shm/test/a.jpg
Run Code Online (Sandbox Code Playgroud)
通过将文件分析为图像来检查文件:
$ linux_czkawka_cli image --directories /run/shm/test/
Found 1 images which have similar friends
/run/shm/test/a.jpg - 1000x1000 - 361.76 KiB - Very High
/run/shm/test/b.jpg - 1000x1000 - 361.76 KiB - Very High
Run Code Online (Sandbox Code Playgroud)
Pet*_*des 11
您可能希望确保对第一个和最后一个 1MiB 左右进行完整比较(或散列),其中元数据可以在不引入压缩数据偏移的情况下进行编辑。此外,从存储中读取的粒度通常至少为 512 字节而不是 16,因此最好这样做;稍微额外的 CPU 时间来比较更多数据是微不足道的。(在 512 字节边界对齐)
(写入扇区大小通常至少为 4096B,但逻辑扇区大小为 512 可能允许 SATA 磁盘仅通过线路发送请求的 512B,如果内核本身不将请求扩大到整页。可能会;页面缓存是在整个页面中管理的。)
请记住,位腐烂是可能的,尤其是当文件存储在 DVD-R 或其他光学介质上时。 如果不检查按位相同(或至少相同的哈希值),我不会删除“重复项” 。根据文件早期部分的哈希签名快速排除重复项很有用,但在大多数情况下,您仍然需要在声明两个文件重复项之前进行全面检查。
如果两个文件几乎相同但有一些位差异,请使用ffmpeg -i foo.mp4 -f null -
查找故障、解码但不对输出执行任何操作。
如果您确实发现了按位差异,但两个文件都没有解码器注意到的错误,请使用
ffmpeg -i foo.mp4 -f framecrc foo.fcrc
Run Code Online (Sandbox Code Playgroud)
或者-f framemd5
查看哪个帧有差异但不是无效的 h.264 流。然后寻找那里并目视检查哪一个已损坏。
您的方法可能适合检测彼此损坏(或元数据编辑)副本的文件,这是普通重复查找器无法轻松做到的。该问题下的评论指出,jdupes
可以在大小比较后使用文件前 N MB 的哈希值,因此这是朝着正确方向迈出的一步。
对于其他用例,也许您可以接受不太严格的检查,但考虑到存在重复文件查找器,仅在存在相同大小的文件时进行比较或散列,您可以让其中一个运行(过夜或当您出去),然后返回完整检查的列表。
有些人fslint
可以选择将重复项彼此硬链接(或符号链接),因此下次您查找重复项时,它们已经是同一个文件。因此,根据我的经验,我认为不需要采取更快但有风险的方法来查找重复文件。
(根据askubuntu 的回答fslint
,Python3 从未更新过,显然是 Rust 的现代克隆。)czkawka
Phi*_*pos 10
GNU 能cmp
帮助你吗?
-s
选项来抑制输出并仅使用返回值-i
(跳过初始)和-n
(要比较的字节数),您可以另外定义要比较的字节范围cmp
如果文件数量对于每对文件来说太大,您可能需要首先按sort
文件大小对所有文件进行比较,然后仅比较具有相同大小的组(uniq -D
使用-w
)。
rsync
看一下rsync
。它有几个级别检查文件是否相同。该手册man rsync
非常详细,您可以识别我所描述的内容,并且可能还有其他一些有趣的替代方案。
最严格的检查是比较每个字节,但是当您写入时,如果数据很多,例如整个备份,则需要花费大量时间。
-c, --checksum skip based on checksum, not mod-time & size
-a, --archive archive mode; equals -rlptgoD (no -H,-A,-X)
Run Code Online (Sandbox Code Playgroud)
标准检查是大小和其他文件属性(例如时间戳)。它通常被认为足够好。
你的想法,@vume,意味着介于这两个检查级别之间的东西。我还没有见过这样的工具,但我对这样的工具非常感兴趣。
vumer
以下 shellscriptvumer
用于dd
执行我认为您想要的操作,@vume。
#!/bin/bash
chunks=16
chnksiz=16
function usage {
echo "Usage: ${0##*/} <file>"
}
if ! test -f "$1"
then
usage
exit
fi
size=$(stat --format='%s' "$1")
step=$(( size/(chunks-1) ))
#echo "step=$step"
tmpfil=$(mktemp)
if [ $size -lt 512 ]
then
chksum=$(md5sum "$1" | sed 's/ .*//')
else
for (( i=0;i<chunks;i++ ))
do
if [ $i -eq $((chunks-1)) ]
then
pos=$((size-chnksiz))
else
pos=$((i*step))
fi
# echo "$i: $pos"
dd if="$1" bs=1 skip=$pos count=16 >> "$tmpfil" 2> /dev/null
done
chksum=$(md5sum "$tmpfil" | sed 's/ .*//')
fi
modif=$(stat --format='%y' "$1")
modif=${modif//\ /_}
echo "size=$size modif=$modif checksum=$chksum file=\"$1\""
#less "$tmpfil"
rm "$tmpfil"
Run Code Online (Sandbox Code Playgroud)
在我的 Lenovo C30 工作站(旧但功能强大)中,我vumer
使用 Ubuntu Desktop 22.04 LTS iso 文件进行了测试,并比较了所用时间md5sum
,
$ time vumer ubuntu-22.04-desktop-amd64.iso
size=3654957056 modif=2022-04-19_10:25:02.000000000_+0200 checksum=ec3483153bfb965745753c4b1b92bf2e file="ubuntu-22.04-desktop-amd64.iso"
real 0m0,024s
user 0m0,018s
sys 0m0,008s
$ time md5sum ubuntu-22.04-desktop-amd64.iso
7621da10af45a031ea9a0d1d7fea9643 ubuntu-22.04-desktop-amd64.iso
real 0m19,919s
user 0m5,331s
sys 0m0,988s
Run Code Online (Sandbox Code Playgroud)
因此,对于大文件来说,它确实比 快得多md5sum
,而如今,后者被认为是一个[太]简单的校验和工具。sha256sum
甚至更慢。
我还检查了 Debian iso 文件,该文件已转换为用其原始文件替换几个启动选项quiet splash
并与其进行比较。运气不好,没有检查修改的几个位置。所以在这里我们必须依靠经典的时间戳来区分。当然能分辨出来。persistence
vumer
md5sum
$ vumer debian-live-10.0.0-amd64-standard.iso
size=865075200 modif=2019-09-05_10:14:31.000000000_+0200 checksum=b8af03a946fb400ca66f2bdb2a6bb628 file="debian-live-10.0.0-amd64-standard.iso"
$ vumer persistent-debian-live-10.0.0-amd64-standard.iso
size=865075200 modif=2019-09-12_18:01:55.000000000_+0200 checksum=b8af03a946fb400ca66f2bdb2a6bb628 file="persistent-debian-live-10.0.0-amd64-standard.iso"
$ md5sum *debian-live-10.0.0-amd64-standard.iso
a64ae643520ca0edcbcc769bae9498f3 debian-live-10.0.0-amd64-standard.iso
574ac1f29a6c86d16353a54f6aa8ea1c persistent-debian-live-10.0.0-amd64-standard.iso
Run Code Online (Sandbox Code Playgroud)
因此,这取决于您拥有什么类型的文件,以及如何修改它们,vumer
以及类似的工具是否有用。
这是扫描目录树的“oneliner”
md5sum $(for i in $(find -type f -ls|sed 's/^ *//'|tr -s ' ' '\t '| \
cut -f 7,11|sort -n|sed 's/\t/\t /'|uniq -Dw10|sed 's/\t */\t/'| \
cut -f 2 );do vumer "$i";done|sed -e 's/.*checksum=//'|sort|rev| \
uniq -f 1 -D|rev|tee /dev/stderr|sed -e 's/.*file=//' -e 's/"//g')|uniq -Dw32
Run Code Online (Sandbox Code Playgroud)
vumer
识别出 30 个文件(15 对)具有相同的 vumer 校验和md5sum
识别出 18 个文件(9 对)具有相同的 md5sum 校验和这意味着vumer
节省了大量时间;md5sum
只需检查 418 个文件中的 30 个。
scan4dblt
我用脚本 替换了“oneliner”,scan4dblt
我也在几个目录树中测试了该脚本,并对“doer”脚本进行了一些编辑vumer
。
#!/bin/bash
function mkfil {
tmpf0=$(mktemp)
tmpf1=$(mktemp)
tmpf2=$(mktemp)
tmpf3=$(mktemp)
tmpf4=$(mktemp)
tmpf5=$(mktemp)
}
function rmfil {
rm "$tmpf0" "$tmpf1" "$tmpf2" "$tmpf3" "$tmpf4" "$tmpf5"
}
# main
echo -n "${0##*/}: "
mkfil
# same size
find -type f -printf "%s %p\n" \
|sed 's/ / /'|sort -n|uniq -Dw11|tee "$tmpf0" |tr -s ' ' ' ' \
|cut -d ' ' -f 2- |sed -e 's/&/\&/g' -e 's/(/\(/g' -e 's/)/\)/g' > "$tmpf1"
res=$(wc -l "$tmpf0"|sed 's/ .*//')
if [ $res -gt 1 ]
then
echo "same size ($res):"
cat "$tmpf0"
else
echo "no files with the same size"
rmfil
exit 1
fi
# vumer
while read fnam
do
echo -n '.'
vumer "$fnam" >> "$tmpf2"
# echo -e "$fnam: $(vumer "$fnam")" >> "$tmpf2"
done < "$tmpf1"
echo ''
sed "$tmpf2" -e 's/.*checksum=//'|sort|uniq -Dw33|tee "$tmpf1" |sed -e 's/.*file=//' -e 's/"//g' > "$tmpf3"
res=$(wc -l "$tmpf1"|sed 's/ .*//')
if [ $res -gt 1 ]
then
echo "vumer: ($res)"
cat "$tmpf1"
else
echo "vumer: no files with the same checksum"
rmfil
exit 1
fi
# inode
while read fnam
do
echo -n '.'
size=$(stat --format='%s' "$fnam")
inod=$(stat --format='%i' "$fnam")
printf "%12d %10d %s\n" $size $inod "$fnam" >> "$tmpf4"
done < "$tmpf3"
echo ''
#cat "$tmpf4"
#cat "$tmpf4" |sort -k3|uniq -f2 -Dw32 |sort -n > "$tmpf5"
cat "$tmpf4" |sort -k2|uniq -f1 -Dw11 |sort -n > "$tmpf5"
> "$tmpf2"
while read fnam
do
if ! grep "$fnam" "$tmpf5" 2>&1 > /dev/null
then
echo "$fnam" >> "$tmpf2"
fi
done < "$tmpf3"
res=$(wc -l "$tmpf5"|sed 's/ .*//')
if [ $res -gt 1 ]
then
echo "inode: ($res)"
echo " size inode md5sum file-name"
cat "$tmpf5"
else
echo "inode: no files with the same inode"
fi
# md5sum
> "$tmpf4"
while read fnam
do
echo -n '.'
size=$(stat --format='%s' "$fnam")
inod=$(stat --format='%i' "$fnam")
printf "%12d %10d " $size $inod >> "$tmpf4"
md5sum "$fnam" >> "$tmpf4"
done < "$tmpf2"
echo ''
#echo "4: ";cat "$tmpf4"
cat "$tmpf4" |sort -k3|uniq -f2 -Dw33 |sort -n > "$tmpf5"
res=$(wc -l "$tmpf5"|sed 's/ .*//')
if [ $res -gt 1 ]
then
echo "md5sum: ($res)"
echo " size inode md5sum file-name"
cat "$tmpf5"
else
echo "md5sum: no files with the same checksum"
rmfil
exit 1
fi
rmfil
Run Code Online (Sandbox Code Playgroud)
scan4dblt
加上示例(输出文件)shellscriptscan4dblt
被进一步开发并使用一些目录树进行测试,包括大 iso 文件、图片、视频剪辑和文档。修复了几个错误(当前版本替换了此处的原始版本)。
例子:
以下示例显示了生成的输出文件
scan4dblt | tee /tmp/scan4dblt.out
Run Code Online (Sandbox Code Playgroud)
尽管一小部分文件由 进行了全面检查md5sum
,但全面检查占用了大部分执行时间。时间比例md5sum
取决于文件大小。
特别是当有很多相对较小的文件时,通过 shellscript 实现效率会很低,编译后的程序会好得多。但对于巨大的文件,例如 iso 文件和视频剪辑,shellscripts 可能会做得很好。
如果我再次进行此练习,我将首先由于硬链接而单独保存双联体,并在列表中保留一个剩余的 [硬链接] 文件,以在以后的比较中检查它是否与另一个文件匹配。
测试应该检查多大的数据块才能[对于vumer
此处调用的工具]做好工作也很有趣。这可能必须针对要检查重复项的文件类型量身定制。
我还将测试哪个文件大小,它对于中间检查有用[by vumer
]。
我很高兴注意到这个问题受到了如此多的关注,包括答案和评论。正如 Peter Cordes 在他的回答(以及评论)中所写,快速测试工具(在我的例子中vumer
)可以根据要测试的文件类型以多种方式进行改进。
在我的回答中,我只实现了 @vume 的原始想法,并且可以表明,在许多情况下,与其他快速排序方法结合使用时它足够好,以最大限度地减少完整校验和测试的需要。
有一个名为imosum的工具,其工作原理与 eg 类似sha256sum
,但它只使用三个 16 kB 块。样本是从文件的开头、中间和结尾处获取的,文件大小也包含在哈希中。
查找重复项的示例用法:
pip install imohash
find .../your_path -type f -exec imosum {} + > /tmp/hashes
sort /tmp/hashes | uniq -w 32 --all-repeated=separate
Run Code Online (Sandbox Code Playgroud)
输出将包含重复文件组:
e8e7d502a5407e75dc1b856024f2c9aa path/to/file1
e8e7d502a5407e75dc1b856024f2c9aa other/path/to/duplicate1
e9c83ccdc726d0ec83c55c72ea151d48 path/to/file2
e9c83ccdc726d0ec83c55c72ea151d48 other/path/to/duplicate2
Run Code Online (Sandbox Code Playgroud)
在我的 SSD 上,处理 72 GB 的数码照片(10k 文件)大约需要 10 秒。