不知何故,我们的一台旧 Server 2008(不是 R2)机器上出现了一个看似无限递归的文件夹。这会对我们的备份造成严重破坏,因为备份代理会尝试递归到该文件夹中,并且永远不会返回。
文件夹结构如下所示:
C:\Storage\Folder1
C:\Storage\Folder1\Folder1
C:\Storage\Folder1\Folder1\Folder1
C:\Storage\Folder1\Folder1\Folder1\Folder1
等等。就像那些曼德布洛集我们九十年代的时候都玩过。
我试过了:
- 从 Explorer 中删除它。是的,我是个乐观主义者。
RMDIR C:\Storage\Folder1 /Q/S
- 这将返回The directory is not empty
ROBOCOPY C:\temp\EmptyDirectory C:\Storage\Folder1 /PURGE
- 在 robocopy.exe 崩溃之前,它会在文件夹中旋转几分钟。
有人能建议一种方法来彻底删除这个文件夹吗?
答案1
感谢大家的有用建议。
深入 StackOverflow 领域,我通过敲出这段 C# 代码解决了这个问题。它使用德利蒙变种Win32专门解决访问长文件路径问题的库。
万一这可以帮助别人,这里是代码 - 它通过了不知何故被困住的~1600 级递归,并花了大约 20 分钟将它们全部删除。
using System;
using Delimon.Win32.IO;
namespace ConsoleApplication1
{
class Program
{
private static int level;
static void Main(string[] args)
{
// Call the method to delete the directory structure
RecursiveDelete(new DirectoryInfo(@"\\server\\c$\\storage\\folder1"));
}
// This deletes a particular folder, and recurses back to itself if it finds any subfolders
public static void RecursiveDelete(DirectoryInfo Dir)
{
level++;
Console.WriteLine("Now at level " +level);
if (!Dir.Exists)
return;
// In any subdirectory ...
foreach (var dir in Dir.GetDirectories())
{
// Call this method again, starting at the subdirectory
RecursiveDelete(dir);
}
// Finally, delete the directory, and any files below it
Dir.Delete(true);
Console.WriteLine("Deleting directory at level " + level);
level--;
}
}
}
答案2
junction
可能是递归连接点。可以使用文件和磁盘实用程序创建此类东西系统内部。
mkdir c:\Hello
junction c:\Hello\Hello c:\Hello
现在您可以无休止地向下浏览 c:\Hello\Hello\Hello....(直到达到 MAX_PATH,对于大多数命令来说是 260 个字符,但对于某些 Windows API 函数来说是 32,767 个字符)。
目录列表显示它是一个连接点:
C:\>dir c:\hello
Volume in drive C is DR1
Volume Serial Number is 993E-B99C
Directory of c:\hello
12/02/2015 08:18 AM <DIR> .
12/02/2015 08:18 AM <DIR> ..
12/02/2015 08:18 AM <JUNCTION> hello [\??\c:\hello]
0 File(s) 0 bytes
3 Dir(s) 461,591,506,944 bytes free
C:\>
要删除,请使用连接实用程序:
junction -d c:\Hello\Hello
答案3
这不是答案,但是我没有足够的代表来发表评论。
我曾经在 MS-DOS 系统上修复过一张当时容量巨大的 500MB FAT16 磁盘上的这个问题。我使用 DOS 调试手动转储并解析目录表。然后我翻转一位以将递归目录标记为已删除。我的 Dettman 和 Wyatt 的《DOS 程序员参考》向我展示了解决方法。
我至今仍对此感到无比自豪。如果有任何通用工具能够如此有效地处理 FAT32 或 NTFS 卷,我会感到惊讶和恐惧。那时的生活更简单。
答案4
chdir
如果您进入目录并只使用相对路径,则不需要长路径名rmdir
。
或者,如果您安装了 POSIX shell,或者将其移植到 DOS 等效版本:
# untested code, didn't bother actually testing since the OP already solved the problem.
while [ -d Folder1 ]; do
mv Folder1/Folder1/Folder1/Folder1 tmp # repeat more times to work in larger batches
rm -r Folder1 # remove the first several levels remaining after moving the main tree out
# then repeat to end up with the remaining big tree under the original name
mv tmp/Folder1/Folder1/.../Folder1 Folder1
rm -r tmp
done
(使用 shell 变量来跟踪循环条件的重命名位置是展开循环的另一种方法,就像我在那里所做的那样。)
这避免了 KenD 解决方案的 CPU 开销,该解决方案强制操作系统在n
每次添加新级别时从顶部到第 级别遍历树,检查权限等。所以它具有sum(1, n) = n * (n-1) / 2 = O(n^2)
时间复杂度。从链开头削减一块的解决方案应该是O(n)
,除非 Windows 在重命名其父目录时需要遍历树。(Linux/Unix 不需要。)chdir
一直到树的底部并从那里使用相对路径,在chdir
目录备份时删除它们的解决方案也应该是O(n)
,假设当您在 CD 上执行操作时,操作系统不需要在每次系统调用时检查所有父目录。
find Folder1 -depth -execdir rmdir {} +
将在 CD 到最深层目录时运行 rmdir。或者实际上,find 的-delete
选项适用于目录,并暗示-depth
。因此find Folder1 -delete
应该做完全相同的事情,但速度更快。是的,Linux 上的 GNU find 通过扫描目录、使用相对路径 CD 到子目录、然后rmdir
使用相对路径、然后 来下降chdir("..")
。它在上升时不会重新扫描目录,因此会消耗O(n)
RAM。
这其实只是一种近似:strace
显示它实际上使用了unlinkat(AT_FDCWD, "tmp", AT_REMOVEDIR)
、open("..", O_DIRECTORY|...)
和fchdir(the fd from opening the directory)
,并且还混入了一堆fstat
调用。但如果在 find 运行时目录树没有被修改,效果是相同的。
编辑:只是为了好玩,我在 GNU/Linux 上尝试了这个(Ubuntu 14.10,2.4GHz 第一代 Core2Duo CPU,WD 2.5TB Green Power 驱动器(WD25EZRS)上的 XFS 文件系统)。
time mkdir -p $(perl -e 'print "annoyingfoldername/" x 2000, "\n"')
real 0m1.141s
user 0m0.005s
sys 0m0.052s
find annoyingfoldername/ | wc
2000 2000 38019001 # 2k lines / 2k words / 38M characters of text
ll -R annoyingfoldername
... eventually
ls: cannot access ./annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername/annoyingfoldername: File name too long
total 0
?????????? ? ? ? ? ? annoyingfoldername
time find annoyingfoldername -delete
real 0m0.054s
user 0m0.004s
sys 0m0.049s
# about the same for normal rm -r,
# which also didn't fail due to long path names
(mkdir -p 创建一个目录和任何缺少的路径组件)。
是的,2k rmdir 操作确实只需要 0.05 秒。xfs 非常擅长在日志中批量处理元数据操作,因为他们在 10 年前就修复了元数据操作缓慢的问题。
在 ext4 上,创建花费 0m0.279 秒,使用查找删除仍花费 0m0.074 秒。