我有两个文件夹/目录:C:\MyData
和C:\MyDataBackup
以及拥有这两个文件夹/目录的人,并且不记得他们是否编辑过原始文件或备份中的文件。
我想摆脱C:\MyDataBackup
,所以我必须找到其中所有与中的兄弟文件相同的文件C:\MyData
并删除它们,然后让所有者手动处理剩余的少数文件。
我该如何实现?到目前为止,我使用的重复检测工具通常存在以下缺点...
- ...搜索重复项里面
C:\MyData
以及C:\MyDataBackup
。这是不允许的!这些文件必须不是删除,因为它们是故意的。而且由于数据堆巨大的,这会导致搜索速度减慢数周。 - ...不进行完整的逐字节比较,而只是依靠哈希总和。
- ...不遵循相同的路径。例如,它们将和标记
C:\MyData\task1\done.txt
为相同。C:\MyDataBackup\task1\done.txt
C:\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
尝试删除当前考虑的文件。当且仅当之前对该文件的所有测试均成功时,才会执行此操作。
可移植性
据我所知,find
Cygwin 中的 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
现在,可变目录(即我们当前的工作目录)仅包含一个最小目录树,其中有可供手动检查的常规文件。
笔记
在很多地方我都用过
--
。如果变量中的路径是绝对的,并且 的起点find
则.
实际上--
不需要。我决定使用,--
以防有人使用这个答案作为灵感并编写--
可能真正有用的代码。find-sh
解释如下:中的第二个 sh 是什么sh -c 'some shell code' sh
?
答案2
Kamil Maciorowski 的回答非常好。
受此启发,我为“find”命令编写了一个脚本,它提供了更多的舒适感和错误检查:
https://github.com/rdiez/Tools/tree/master/DeleteFilesIfDuplicatedInReferenceDir