技术说明
大多数方法引起问题的原因是Windows尝试枚举文件和文件夹。几百个甚至几千个文件/文件夹只有几层深度,这并不是什么大问题,但是当您在数百万个文件夹中有数万亿个文件进入数十个级别时,那肯定会使系统瘫痪。
让我们“仅”拥有100,000,000个文件,Windows使用这样的简单结构来存储每个文件及其路径(这样可以避免分别存储每个目录,从而节省了一些开销):
struct FILELIST { // Total size is 264 to 528 bytes:
TCHAR name[MAX_PATH]; // MAX_PATH=260; TCHAR=1 or 2 bytes
FILELIST* nextfile; // Pointers are 4 bytes for 32-bit and 8 for 64-bit
}
根据使用8位字符还是Unicode字符(使用Unicode)以及系统是32位还是64位,这将需要25GB到49GB的内存来存储列表(这非常简化的结构)。
原因 ,为什么 Windows会尝试删除前枚举文件和文件夹取决于您使用删除它们的方法,但两者资源管理器和命令解释器做(你可以看到的延迟,当您启动命令)。您还可以看到磁盘活动(HDD LED)闪烁,因为它从驱动器读取目录树。
解
解决这种情况的最佳选择是使用删除工具,该工具可以一次删除一个文件和文件夹。我不知道是否有任何现成的工具可以执行此操作,但是应该可以通过一个简单的批处理文件来完成。
@echo off
if not [%1]==[] cd /d %1
del /q *
for /d %%i in (*) do call %0 "%%i"
这是为了检查是否传递了参数。如果是这样,则它将更改为指定的目录(您可以在不带参数的情况下运行它以在当前目录中启动或指定目录,甚至在其他驱动器上也可以从该目录开始)。
接下来,它将删除当前目录中的所有文件。在这种模式下,它不应该枚举任何东西,而只是删除文件而不会占用太多(如果有的话)内存。
然后枚举当前目录中的文件夹并调用自身,将每个文件夹传递给它(自己)以向下递归。
分析
之所以应该起作用,是因为它没有枚举整个树中的每个文件和文件夹。它根本不枚举任何文件,而仅枚举当前目录中的文件夹(加上其余的父目录中)。假设任何给定的文件夹中只有几百个子目录,那么这应该不会太糟,并且与枚举整个树的其他方法相比,所需的内存肯定要少得多。
您可能想知道使用/r
开关而不是使用(手动)递归。那是行不通的,因为当/r
开关进行递归时,它会预先枚举整个目录树,而这正是我们想要避免的。我们想删除而不追踪。
比较方式
让我们将此方法与完全枚举方法进行比较。
您曾说过您有“数百万个目录”;假设是1亿。如果树是大致平衡的,并且假设每个文件夹平均大约有100个子目录,那么最深的嵌套目录将向下大约四个级别-实际上,整个树中将有101,010,100个子文件夹。(有趣的是100M如何分解为100和4。)
由于我们不枚举文件,因此我们仅需要跟踪每个级别最多100个目录名,即可4 × 100 = 400
在任何给定时间最多跟踪目录。
因此,内存要求应为〜206.25KB,并且在任何现代(或其他方式)系统的限制之内。
测试
不幸的是(?)我没有一个在数百万个文件夹中包含数万亿个文件的系统,所以我无法对其进行测试(我相信最后一次计数,我有大约80万个文件),因此其他人将不得不尝试它。
警告
当然,内存并不是唯一的限制。该驱动器也将成为一个大瓶颈,因为对于您删除的每个文件和文件夹,系统都必须将其标记为空闲。值得庆幸的是,许多磁盘操作将被捆绑在一起(缓存)并成块而不是单独写出(至少对于硬盘驱动器,而不是可移动媒体),但是当系统读取时,它仍然会引起相当大的麻烦。并写入数据。