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

Ken*_*enD 45 windows-server-2008 filesystems

不知何故,我们的一个旧 Server 2008(不是 R2)机器开发了一个看似无限循环的文件夹。这对我们的备份造成了严重破坏,因为备份代理试图向下递归到文件夹中并且永不返回。

文件夹结构类似于:

C:\Storage\Folder1
C:\Storage\Folder1\Folder1
C:\Storage\Folder1\Folder1\Folder1
C:\Storage\Folder1\Folder1\Folder1\Folder1
Run Code Online (Sandbox Code Playgroud)

... 等等。这就像我们在 90 年代玩过的那些Mandelbrot 套装之一。

我试过了:

  • 从资源管理器中删除它。是的,我是一个乐观主义者。
  • RMDIR C:\Storage\Folder1 /Q/S - 这返回 The directory is not empty
  • ROBOCOPY C:\temp\EmptyDirectory C:\Storage\Folder1 /PURGE - 在 robocopy.exe 崩溃之前,这会在文件夹中旋转几分钟。

任何人都可以建议一种永久关闭此文件夹的方法吗?

Ken*_*enD 45

感谢大家的有用建议。

进入 StackOverflow 领域后,我通过敲出这段 C# 代码解决了这个问题。它使用Delimon.Win32.IO库,专门解决访问长文件路径的问题。

以防万一这可以帮助其他人,这是代码 - 它通过了大约 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--;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 递归到一个函数 1600 次?[堆栈溢出](http://en.wikipedia.org/wiki/Stack_overflow) 确实是领土! (8认同)
  • 哇,很高兴你最终解决了这个问题。仅供参考,微软官方支持的这种情况的修复方法是“重新格式化卷”。是的,认真的。:/ (8认同)
  • 你有没有想过那是怎么发生的?听起来像一些写得很糟糕的用户程序的错。 (4认同)
  • 我试过,甚至使用了`.Delete` 的Delimon 版本(而不是普通的`System.IO` 版本),虽然它没有抛出异常,但它似乎没有做任何事情。当然,使用上述方法的递归需要很长时间,而`.Delete` 只占用了 5-10 秒的时间。也许它在几个目录中消失了然后放弃了? (2认同)
  • 顺便说一句,文件夹中是否填充了任何东西?如果您可以确定创建递归文件夹的时间间隔并将其乘以递归次数,您将(希望)得到这个问题何时开始的粗略时间表...... (2认同)

Bri*_*ian 25

可能是递归连接点。可以使用Sysinternalsjunction的文件和磁盘实用程序创建这样的东西。

mkdir c:\Hello
junction c:\Hello\Hello c:\Hello
Run Code Online (Sandbox Code Playgroud)

您现在可以无休止地向下运行 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:\>
Run Code Online (Sandbox Code Playgroud)

要删除使用连接实用程序:

junction -d c:\Hello\Hello
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,`DIR` 只是向我展示了普通的 ole 目录 - 恐怕没有任何连接的迹象 (4认同)
  • `没有找到重解析点`:( (3认同)
  • 我想知道是什么造成了如此混乱的实际子目录。 (3认同)
  • 你能用 `junction -s C:\Storage\Folder1 ` 做一个快速的双重检查吗? (2认同)
  • 使用 `dir /a` 来查看 '&lt;JUNCTION&gt;' 而不指定具体名称。 (2认同)

小智 17

不是答案,但我没有足够的代表发表评论。

我曾经在 MS-DOS 系统上的当时巨大的 500MB FAT16 光盘上解决了这个问题。我使用DOS调试手动转储和解析目录表。然后我翻转了一点以将递归目录标记为已删除。我的 Dettman 和 Wyatt 'DOS Programmers' Reference' 给我指明了方向。

我仍然为此感到无比自豪。如果有任何通用工具对 FAT32 或 NTFS 卷具有如此强大的能力,我会感到惊讶和害怕。那个时候的生活比较简单。

  • 我会说你*有理由*为此感到自豪。 (8认同)
  • +1 对我有一些代表。不错的解决方案。 (3认同)
  • 您现在可以删除答案的第一句话。 (3认同)

Spi*_*Pig 8

Java 还可以处理长文件路径。它也可以做得更快。这段代码(我从 Java API 文档中复制的)将在大约 1 秒内删除一个 1600 级深度的目录结构(在 Windows 7、Java 8.0 下)并且没有堆栈溢出的风险,因为它实际上不使用递归。

import java.nio.file.*;
import java.nio.file.attribute.*;
import java.io.*;

public class DeleteDir {

  static void deleteDirRecur(Path dir) throws IOException {
    Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
         @Override
         public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
             throws IOException
         {
             Files.delete(file);
             return FileVisitResult.CONTINUE;
         }
         @Override
         public FileVisitResult postVisitDirectory(Path dir, IOException e)
             throws IOException
         {
             if (e == null) {
                 Files.delete(dir);
                 return FileVisitResult.CONTINUE;
             } else {
                 throw e;
             }
         }
     });
  }

  public static void main(String[] args) throws IOException {
    deleteDirRecur(Paths.get("C:/Storage/Folder1"));
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 这些天我主要是一个 Windows 人,所以是的,是的。由于必须在我雇主的环境中为近 1,000 个客户端维护 5 个特定的、不同版本的 JRE,其中一个可以追溯到 2009 年,由于垃圾...... ...呃,我们用于关键业务应用程序的“企业级”软件套件。 (5认同)
  • 我恨你。你强迫我对一个涉及使用 Java 的答案投赞成票,现在我觉得很脏。我需要冲凉先。 (3认同)

Pet*_*des 6

如果您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
Run Code Online (Sandbox Code Playgroud)

(使用 shell 变量来跟踪您为循环条件重命名它的位置是像我在那里那样展开循环的另一种选择。)

这避免了 KenD 解决方案的 CPU 开销,这会迫使操作系统在n每次添加新级别时从顶层遍历树,检查权限等。因此它具有sum(1, n) = n * (n-1) / 2 = O(n^2)时间复杂度。从链的开头削减一个块的解决方案应该是O(n),除非 Windows 在重命名其父目录时需要遍历树。(的Linux / Unix没有。)解决方案,chdir一路下跌到树的底部,并使用从那里相对路径,删除目录,因为它们chdir备份,也应该是O(n),假设OS并不需要检查所有父目录每个系统调用,当你在某个地方 CD 做事情时。

find Folder1 -depth -execdir rmdir {} +将在 CD 到最深目录时运行 rmdir。或者实际上,find 的-delete选项适用于目录,并暗示-depth. 所以find Folder1 -delete应该做完全相同的事情,但速度更快。是的,Linux 上的 GNU find 是通过扫描目录、CDing 到具有相对路径的子目录,然后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
Run Code Online (Sandbox Code Playgroud)

(mkdir -p 创建一个目录和任何缺少的路径组件)。

是的,2k rmdir ops 真的是 0.05 秒。xfs 非常擅长在日志中批量处理元数据操作,因为它们修复了元数据操作像 10 年前一样缓慢的问题。

在 ext4 上,create 花费了 0m0.279s,使用 find 删除仍然花费了 0m0.074s。