OP 的 @vume 的想法的 Shellscript 实现

OP 的 @vume 的想法的 Shellscript 实现

在处理 jpg 或 h264 压缩文件时,像 fdupes 这样的工具是荒谬的矫枉过正。两个具有完全相同文件大小的此类文件已经很好地表明它们是相同的。

除此之外,如果提取并比较 16 个 16 字节的等距块,并且它们也相同,那么就有足够的证据让我假设它们是相同的。有这样的事吗?

(顺便说一句,我知道文件大小本身可能是一个相当不可靠的指标,因为可以选择压缩到某些目标大小,例如 1MB 或 1 CD/DVD。如果在许多文件上使用相同的目标大小,这是相当合理的一些不同的文件将具有完全相同的大小。)

答案1

茨考卡是一个开源工具,旨在查找重复文件(以及图像、视频或音乐)并通过命令行或图形界面呈现它们,重点是速度。这部分来自文档您可能感兴趣:

更快地扫描大量重复项

默认情况下,按相同大小分组的所有文件都会计算部分哈希值(每个文件仅 2KB 的哈希值)。这种哈希值的计算速度通常非常快,尤其是在 SSD 和快速多核处理器上。但是,当使用 HDD 或慢速处理器扫描数十万或数百万个文件时,此步骤通常可能需要很长时间。

在 GUI 版本中,哈希值将存储在缓存中,以便以后搜索重复项会更快。


例子:

创建一些测试文件:

我们生成随机图像,然后复制a.jpgb.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

仅检查尺寸:

$ 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

通过哈希值检查文件:

$ 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

通过将文件分析为图像来检查文件:

$ 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

答案2

您可能需要确保对第一个和最后一个 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

或者-f framemd5查看哪个帧有差异但不是无效的 h.264 流。然后寻找那里并目视检查哪一个已损坏。

你的方法可能适合检测彼此损坏(或经过元数据编辑)副本的文件,这是普通重复查找器不容易做到的事情。该问题下的评论指出jdupes可以使用文件前 N MB 的哈希值经过尺寸比较后,这是朝着正确方向迈出的一步。


对于其他用例,也许您可​​以接受不太严格的检查,但考虑到存在重复文件查找器,仅在存在相同大小的文件时进行比较或散列,您可以让其中一个运行(过夜或当您出去),然后返回完整检查的列表。

有些工具fslint可以选择将重复文件硬链接在一起(或符号链接),这样下次您查找重复文件时,它们就已经是同一个文件了。因此,根据我的经验,我并不认为查找重复文件需要采取更快但有风险的方法。

fslint显然从未更新过 Python3czkawka是 Rust 的现代克隆,根据阿斯库本图答案.)

答案3

GNU 能cmp帮助你吗?

  • 您可以使用该-s选项来抑制输出并仅使用返回值
  • 它首先检查文件大小以跳过对不同文件大小的任何比较
  • 使用选项-i(跳过初始)和-n(要比较的字节数),您可以另外定义要比较的字节范围

如果文件数量对于cmp每对文件来说太大,您可能需要首先sort按文件大小对所有文件进行比较,然后仅比较具有相同大小的组(uniq -D使用-w)。

答案4

OP 的 @vume 的想法的 Shellscript 实现

背景与示例rsync

看一下rsync。它有几个级别检查文件是否相同。该手册man rsync非常详细,您可以识别我所描述的内容,并且可能还有其他一些有趣的替代方案。

  • 最严格的检查是比较每个字节,但是当您写入时,如果数据很多,例如整个备份,则需要花费大量时间。

    -c, --checksum              skip based on checksum, not mod-time & size
    -a, --archive               archive mode; equals -rlptgoD (no -H,-A,-X)
    
  • 标准检查是大小和其他文件属性(例如时间戳)。它通常被认为足够好。

  • 你的想法,@vume,意味着介于这两个检查级别之间的东西。我还没有见过这样的工具,但我对这样的工具非常感兴趣。

编辑1:shell脚本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"

在我的 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

因此,对于大文件来说,它确实比 快得多md5sum,而如今,后者被认为是一个[太]简单的校验和工具。sha256sum甚至更慢。

quiet splash我还检查了 Debian iso 文件,该文件已转换为用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

因此,这取决于您拥有什么类型的文件,以及如何修改它们,vumer以及类似的工具是否有用。

编辑 2:扫描目录树的“oneliner”

这是扫描目录树的“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
  • 目录树中有 418 个文件(包含 iso 文件和一些其他文件)
  • 检查已识别的大小 72 个文件(可能是 36 对)
  • vumer识别出 30 个文件(15 对)具有相同的 vumer 校验和
  • md5sum识别出 18 个文件(9 对)具有相同的 md5sum 校验和

这意味着vumer节省了大量时间;md5sum只需检查 418 个文件中的 30 个。

编辑3:shell脚本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

编辑4:改进的shellscriptscan4dblt加上示例(输出文件)

shellscriptscan4dblt经过进一步开发,并使用几个目录树进行了测试,包括大型 iso 文件、图片、视频剪辑和文档。已修复几个错误(当前版本替换了此处的原始版本)。

例子:

以下示例显示了生成的输出文件

scan4dblt | tee /tmp/scan4dblt.out

尽管一小部分文件由 进行了全面检查md5sum,但全面检查占用了大部分执行时间。时间比例md5sum取决于文件大小。

特别是当有很多相对较小的文件时,通过 shellscript 实现的效率会很低,编译后的程序会好得多。但对于巨大的文件,例如 iso 文件和视频剪辑,shellscripts 可能会做得很好。

编辑 5:关于 shellscript 的附加评论

如果我再次进行此练习,我将首先由于硬链接而单独保存双联体,并在列表中保留一个剩余的 [硬链接] 文件,以在以后的比较中检查它是否与另一个文件匹配。

vumer测试应该检查多大的数据块才能[对于此处调用的工具]做好工作也很有趣。这可能必须针对要检查重复项的文件类型量身定制。

我还将测试哪个文件大小,它对于中间检查有用[by vumer]。

最后(?)评论

我很高兴注意到这个问题受到了如此多的关注,包括答案和评论。正如 Peter Cordes 在他的回答(以及评论)中所写,快速测试工具(在我的例子中vumer)可以根据要测试的文件类型以多种方式进行改进。

在我的回答中,我只实现了 @vume 的原始想法,并且可以表明,在许多情况下,与其他快速排序方法结合使用时它足够好,以最大限度地减少完整校验和测试的需要。

相关内容