如何合并两个文件夹以便从一个文件夹中删除相同的文件,同时保留校验和差异?

如何合并两个文件夹以便从一个文件夹中删除相同的文件,同时保留校验和差异?

我遇到这样一种情况,我需要从可能来自不同时间点的驱动器进行恢复,该时间点不仅在文件存在方面可能有所不同,而且存在一些损坏,其中许多文件明显已损坏。

我们将左侧文件夹命名为“A”,右侧文件夹命名为“B”。

我有责任合并这两个图像,这样:

  1. B 中存在但 A 中不存在的任何文件都应移动到 A,并且
  2. 两个位置中都存在且相同的任何文件都应从 B 中删除,最后
  3. 任何校验和不同的文件都应保留在 B 中,以便可以在 A 和 B 之间手动比较这些不同的文件,但除了这些具有不同校验和(即实际内容)的文件外,B 中不应保留任何其他内容。

笔记:在这一点上,日期几乎不重要,尽管它会好的在元数据中保留较旧的日期。

这怎么能干干净净地完成呢?不幸的是,我必须对数十 TB 的数据执行此操作,因此如果我不知道如何自动化此操作,这将是一个非常漫长的过程。看来 90-95% 的内容是相同的,因此应该制定一种“设置后就忘记它”的方法,为进行手动比较做好准备。

答案1

第 2 步和第 3 步似乎是最困难的,所以让我们从这些开始。

有一个名为的工具rdfind可以查找重复文件。您可以决定在检测到重复项时该怎么做:在您的情况下,您希望将其从 B: 中删除rdfind -deleteduplicates true A B。如果 A 和 B 中存在相同的文件,则保留 A 中的文件。其他选项是用硬链接或软链接替换副本,或者只是报告结果。

然后,保留在 B 中的文件要么是 B 所独有的,要么 B 中的文件与 A 中的不同。 将唯一的文件从 B 移动到 A:mv -i B/* A/no在每次询问是否要覆盖时回答。您可以使用 自动化此操作yes no | mv -i B/* A/。如果您使用 GNU mv,则可以使用mv --no-clobber B/* A/.

当然,在对真实数据进行操作之前,您需要先进行练习。您可以轻松地在 A 和 B: 中创建指向文件的硬链接树mkdir training; cp -lr A training; cp -lr B training,并在那里进行练习。

答案2

这里有一个方法,虽然简单,但是如果A中缺少很多文件,效率就很低。只需依次执行每个步骤即可。我假设只有目录和常规文件(比较特殊文件的元数据可以通过更多的工作来完成)。警告:未经测试的代码。

首先,将 B 中存在但 A 中不存在的文件复制到 A。尽可能保留元数据(时间戳、权限)。

rsync -a --ignore-existing B A

其次,从 B 中删除重复项。请注意,此时,最初 A 中不存在的文件现在是相同的。

cd B
find . -type f -exec sh '
  for x; do
    if cmp -s "$x" "$0/$x"; then rm "$x"; fi
  done
' /path/to/A {} +

(可选)从 B 中删除空目录。

find B -depth -type d -exec rmdir {} + 2>/dev/null

这是低效的,因为在步骤 2 中,A 中已经缺失的每个文件现在都会被复制并进行比较,然后从 B 中删除。如果 A 中缺失很多文件,则对 B 进行一次传递会更有效将文件移动到 A 并删除重复项。如果 A 和 B 位于同一文件系统上,则尤其如此,这样可以便宜地移动文件,而不是通过复制然后删除源。

答案3

我首先会挑战您的要求。你正试图一步完成所有事情。在开始恢复文件之前,您最好知道恢复后的系统是什么样子。

实际上,首先获得差异比您想象的要容易。

步骤1

获取磁盘上每个文件的哈希值。无论如何你都必须这样做。所以不妨把它结束并完成。 如果没有太多硬链接,下面的命令效果很好。假设目录名为/media/A/media/B

cd /media/A
find . -type f -exec sha256sum {} + > ~/hashes.txt

这将为磁盘上的每个常规文件创建一个哈希值。如果文件是硬链接的,它将出现在每个名称下(并且已为每个名称扫描一次)。

第2步

识别变化

cd /media/B
sha256sum -c ~/hashes.txt > ~/check.txt

check.txt 现在将包含三种类型的行:

  • good/file: OK
  • missing/file: FAILED open or read
  • changed/file: FAILED

步骤3

作为快捷方式,您可以使用以下命令复制所有丢失的文件:

rsync -a --ignore-existing /media/A/ /media/B/

步骤4

那么你只需要担心改变的文件:

grep 'FAILED$' ~/check.txt | while read file ; do
    echo "${file%: FAILED}"
done > ~/changed.txt

这将为您提供changed.txt,其中每一行都有一个文件名。每一个都是两个系统上已更改的文件。

现在由您来排序changed.txt并确定要保留哪些文件以及将哪些文件从 B 覆盖到 A。

答案4

假设文件名中没有任何“换行符”,这应该可以工作:

cd B
find . -type f -print | while read f
do
    [[ -f "A/$f" ]] || { echo mv "$f" "A/$f" ; continue; }
    cmp "$f" "A/$f" && echo rm "$f"
done

运行它,如果看起来不错,请删除“echo”字样以运行实际的命令。

相关内容