查看多个 zip 文件中的任何一个是否包含目录结构中的任何原始文件

查看多个 zip 文件中的任何一个是否包含目录结构中的任何原始文件

我这里有一个非常困难的问题。

我有一个照片库,其中的各个文件夹中有很多照片。

然后我开始使用 Google Photos 来拍摄我的照片,我将这些原件放入 Google Photos 中,并使用了 5 年多。

现在我想放弃 Google Photos。我已经对我的所有照片进行了 Google 导出,并下载了所有 Zip 文件,其中价值约 1.5TB(150 x 约 10GB 文件)。

现在我想保留原来的目录结构,并删除 Google Photos 中所有重复的文件。在此操作之后,我基本上希望留下两个目录,每个目录中都包含唯一的文件。然后我可以稍后手动合并它。

我已经开始提取所有文件,然后我将运行rmlint以检测重复项并从 Google Drive 中清除。问题是我没有足够的空间来操作所有这些,所以我必须提取 30 个档案,然后运行rmlint、清除、提取另外 30 个、rmlint再次运行、清除等。这会一遍又一遍地重新扫描我的原始文件,这将需要很长时间才能完成。我已经使用--xattrrmlint 标志来尝试加速后续运行。完整rmlint命令请参见附录。

我怎样才能做到这一点而不必先提取所有档案?有没有办法只使用 zip 文件中的文件校验和并与这些文件进行比较?

谢谢!

附录

rmlint \
        --xattr \
        -o sh:rmlint-photos.sh \
        -o json:rmlint-photos.json \
        --progress \
        --match-basename \
        --keep-all-tagged \
        --must-match-tagged \
        "/mnt/f/GoogleTakeout/" \
        // \
        "/mnt/e/My Documents/Pictures/" \

答案1

在 zsh 或 bash 以及 libarchivebsdtar和 GNU中tar,您可以执行以下操作:

LC_ALL=C find . -name '*.zip' -type f -print0 |
  while IFS= read -rd '' archive; do
    bsdtar -cf - "@$archive" |
      ARCHIVE=$archive tar -xf - --to-command='
        case $TAR_FILETYPE$TAR_FILENAME in
          (f*.jpg | f*.JPG)
            sha1sum | {
              IFS= read -r sum rest &&
                printf "%s\n" "$sum:$ARCHIVE:$TAR_FILENAME"
            }
        esac' > sums.txt

(假设存档路径和成员都不包含换行符或:字符)获取每个存档的列表<checksum>:<archive>:<file-in-archive>(需要一段时间,因为sha1sum每个图像运行一个)。

然后,您可以为每个唯一的校验和仅提取一个文件,如下所示:

perl -F: -slane '
  ($sum, $archive, $file) = @F;
  if (!$seen{$sum}++) {
    push @{$files{$archive}}, $file;
  }
  END {
    for $archive (keys %files) {
      open EXTRACT, "|-", "bsdtar", "-C", $dest, "-T", "/dev/stdin", "-xvnf", $archive;
      for (@{$files{$archive}}) {
        s/[][?*\\]/\\$&/g; # escape wildcards
        print EXTRACT;
      }
    }
  }' -- -dest='/mnt/e/My Documents/Pictures/' sums.txt

(您可能需要添加更多错误处理)。

perl但是同时进行校验和提取可能会更有效:

(LC_ALL=C find "$PWD" -name '*.zip' -type f -print0 | {
  cd '/mnt/e/My Documents/Pictures' &&
    perl -MArchive::Zip -MDigest::SHA=sha1 -0lne '
      my $zip = Archive::Zip->new();
      if ($zip->read($_) == AZ_OK) {
        for $member ($zip->membersMatching(".*\.jpe?g")) {
          $zip->extractMember($member)
            unless (!$seen{sha1(zip->contents($member))}++);
        }
      }'
)

(警告:这一切都未经测试)。

相关内容