我可以找到在不同位置具有相同路径的重复文件吗?

我可以找到在不同位置具有相同路径的重复文件吗?

我有两个文件夹/目录:C:\MyDataC:\MyDataBackup以及拥有这两个文件夹/目录的人,并且不记得他们是否编辑过原始文件或备份中的文件。

我想摆脱C:\MyDataBackup,所以我必须找到其中所有与中的兄弟文件相同的文件C:\MyData并删除它们,然后让所有者手动处理剩余的少数文件。

我该如何实现?到目前为止,我使用的重复检测工具通常存在以下缺点...

  • ...搜索重复项里面 C:\MyData以及C:\MyDataBackup。这是不允许的!这些文件必须不是删除,因为它们是故意的。而且由于数据堆巨大的,这会导致搜索速度减慢数周。
  • ...不进行完整的逐字节比较,而只是依靠哈希总和。
  • ...不遵循相同的路径。例如,它们将和标记C:\MyData\task1\done.txt为相同。C:\MyDataBackup\task1\done.txtC:\MyDataBackup\task57\done.txt

那么,我该如何进行重复搜索

  • 在两个文件夹/目录中,仅找到两者之间的配对,而不是每个文件夹/目录中的配对
  • 进行完整比较(逐字节)
  • 是否限制在相应文件夹/目录内的相同路径?

我使用的是 Windows,但有 Cygwin,所以我也可以使用 bash magic。

(我今天也在 StackOverflow 上偶然发布了这个问题)

答案1

初步说明

首先在一些可用的目录对上测试该解决方案。


解决方案

此答案使用 *nix 工具。它应该在 Cygwin 中工作。我的意思是在 Cygwin 提供的 shell(如bash)中。(shell 很重要,请参阅这个问题

成为干燥,我将使用 shell 变量。如果您需要将此答案应用于其他目录,那么只需更改变量即可,而后面的命令是静态的。使用绝对路径。运行此代码片段来设置变量:

reference='/cygdrive/c/MyData'
mutable='/cygdrive/c/MyDataBackup'

(在这种特殊情况下,单引号不是必需的;但是,没有经验的用户如果想处理名称中带有空格的目录,可能会喜欢将引号放在正确的位置。)

您需要cd进入可变目录。如果以下命令因任何原因失败,请中止。

cd -- "$mutable"

这是一个执行实际工作的命令:

find . -type f \
       -print \
       -exec test -f "$reference"/{} \; \
       -exec cmp -- {} "$reference"/{} \; \
       -delete

解释

  • .定义我们的起点,当前工作目录。由于先前的原因,cd这将是可变目录。我们不使用它"$mutable"作为起点,因为我们需要find考虑相对路径,以便稍后将它们与参考目录的路径连接起来。我们find将尝试测试所有文件在 (包括) 之下.,下降到任意深度的子目录。

  • -type f是检查当前考虑的文件是否为常规文件的测试。此测试的目的是避免cmp稍后提供其他类型的文件。例如,我们不想cmp与目录一起使用。

  • -print打印当前考虑的文件的路径名。这只是为了指示进度;-print如果您愿意,可以省略。

  • -exec test -f "$reference"/{} \;测试参考目录中是否存在相同相对路径下的常规文件。在 GNU 手册中find -exec … ;被描述为操作,但它也是一个测试:成功当且仅当被调用的可执行文件(此处test)返回退出状态0,这就是我们在此所依赖的。我们的测试不仅是为了避免将意外类型的文件提供给cmp后续程序;还是为了:

    • 避免将不存在的文件交给cmp
    • 避免给出符号链接cmp(见下文)。
  • -exec cmp -- {} "$reference"/{} \;是一个实际比较两个文件的测试。注意,如果cmp给出了符号链接和符号链接的目标,那么它会告诉您内容是相同的。就您的问题而言:如果foo在参考目录中有一个指向foo可变目录的符号链接,那么cmp我们会认为有两个副本,而唯一的副本在可变目录中,如果我们盲目相信,cmp那么我们会删除它。不提供符号链接cmp(见上文)可以解决这个问题。

  • -delete尝试删除当前考虑的文件。当且仅当之前对该文件的所有测试均成功时,才会执行此操作。


可移植性

据我所知,findCygwin 中的 GNU 是find支持的,-delete这是一个不可移植的扩展。GNU还支持扩展中的find多个,以及扩展与某个字符串的连接;这些功能不可移植。如果您需要可移植的解决方案,请使用以下代码片段。它是上述内容的替代方案,而不是补充。{}-exec{}

find . -type f \
       -exec sh -c '
          reference="$1"
          shift
          for f; do
             printf "%s\\n" "$f"
             test -f "$reference/$f" \
             && cmp -- "$f" "$reference/$f" \
             && rm -- "$f"
          done
       ' find-sh "$reference" {} +

合理添加

接下来你可能想要删除空目录来自可变目录:

find . -type d -empty -delete

-empty并且不可移植。用+-delete替换相对容易,-delete-depth-exec rmdir -- {} \;不太容易被取代-empty,我就不多说了。

也许你还想删除符号链接等。以下命令尝试删除文件(不包括目录和常规文件):

find . ! -type d ! -type f -delete

现在,可变目录(即我们当前的工作目录)仅包含一个最小目录树,其中有可供手动检查的常规文件。


笔记

  • 一般来说,存在竞争条件(托克托)可能会让恶意用户迫使你删除错误目录中的文件。例如,参见竞争条件-exec

  • 在很多地方我都用过--。如果变量中的路径是绝对的,并且 的起点find.实际上--不需要。我决定使用,--以防有人使用这个答案作为灵感并编写--可能真正有用的代码。

  • find-sh解释如下:中的第二个 sh 是什么sh -c 'some shell code' sh

答案2

Kamil Maciorowski 的回答非常好。

受此启发,我为“find”命令编写了一个脚本,它提供了更多的舒适感和错误检查:

https://github.com/rdiez/Tools/tree/master/DeleteFilesIfDuplicatedInReferenceDir

相关内容