删除(显然)无限递归文件夹

删除(显然)无限递归文件夹

不知何故,我们的一台旧 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 秒。

相关内容