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


46

不知何故,我们的旧版Server 2008(不是R2)盒中的一个已经开发了一个看似无限递归的文件夹。由于备份代理试图向下递归到该文件夹​​中并且永不返回,因此这与备份有关。

文件夹结构如下所示:

C:\Storage\Folder1
C:\Storage\Folder1\Folder1
C:\Storage\Folder1\Folder1\Folder1
C:\Storage\Folder1\Folder1\Folder1\Folder1

... 等等。就像是我们90年代曾经玩过的其中一套曼德布罗特组合

我试过了:

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

谁能建议永久杀死该文件夹的方法?


1
我会尝试/MIRROBOCOPY /MIR C:\temp\EmptyDirectory C:\Storage\Folder1也许值得一试chkdsk
jscott 2015年

1
/MIR似乎可以使用更长的时间,但最终也被炸了(“复印机已停止工作”)。我有点害怕做chkdsk; 这是一台非常老的服务器,我担心这个问题表明文件系统出现了更大的问题……
KenD

7
尝试从Linux(Ubuntu / Centos / Fedora / ...)桌面试用CD引导,然后从那里删除文件夹。
Guntram Blohm

2
@KenD如果您怀疑文件系统损坏问题,则当然应该首先尝试修复文件系统。尝试删除目录的技巧可能会使情况变得更糟。
jscott 2015年

1
由于(从下面的答案中),目录不是无限的,只是非常深的,如果您安装了CygWinUnxUtils,则可以使用find深度优先的目录删除:find Storage/Folder1 -depth -exec rmdir {} \;
Johnny

Answers:


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--;
        }
    }
}

2
我尝试过,甚至使用的是Delimon版本.Delete(而不是普通System.IO版本),尽管它没有引发异常,但似乎没有任何作用。当然,使用上述方法进行递归要花很长时间,并且.Delete只咀嚼事物5-10秒。也许它消失在几个目录然后放弃了?
KenD

4
您是否知道这是怎么开始的?听起来像某些写得不好的用户界面程序的错误。
Parthian Shot 2015年

8
递归为一个函数1600次?确实是堆栈溢出区域!
Aleksandr Dubinsky

2
顺便说一句,文件夹中是否塞满了东西?如果您可以确定递归文件夹的创建时间间隔,然后乘以递归次数,则(希望)可以大致确定该问题开始的时间范围...
Get-HomeByFiveOClock 2015年

8
哇,很高兴您最终解决了这个问题。仅供参考,Microsoft对这种情况的官方支持修复是“重新格式化卷”。是的,认真。:/
HopelessN00b

25

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

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

4
不幸的是,这DIR只是向我显示了普通的ole目录-恐怕没有任何路口的迹象
KenD

2
您可以快速进行仔细检查junction -s C:\Storage\Folder1 吗?
布赖恩

3
No reparse points found:(
KenD

3
我不知道是什么造成了如此混乱的实际子目录。
布赖恩

2
dir /a看“<JUNCTION>”没有指定具体的名称。
Chloe

16

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

我曾经在MS-DOS系统上一块当时很大的500MB FAT16光盘上解决了这个问题。我使用DOS调试手动转储和分析目录表。然后,我翻转了一点以将递归目录标记为已删除。我的Dettman和Wyatt的“ DOS程序员参考”副本为我提供了方法。

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


8
我会说,你是有理由为此感到自豪。
mfinni 2015年

3
+1代表我。不错的解决方案。
克里斯·桑顿

3
您现在可以删除答案的第一句话。
AL

这不是答案。这是关于不同的操作系统和不同的文件系统的故事。除了对这个(NT,NTFS)问题没有帮助之外,它甚至对同样的问题(DOS,FAT16)也没有帮助,因为它实际上不包含任何细节。
安德鲁·梅迪科

@Andrew Medico:我同意你的看法,因此我的第一句话。但我确实告诉您在哪里可以找到信息以解决稍有相关的问题。
理查德

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"));
  }
}

3
我恨你。您已经强迫我提高一个涉及使用Java的答案,现在我感觉很脏。我需要冲凉先。
HopelessN00b

我很抱歉。希望您最终能从创伤中恢复过来。但是Microsoft(c#)开发的语言真的好得多吗?
SpiderPig

5
这些天我主要是Windows人士,是的,是的。由于不得不在雇主环境中的近1,000个客户端中维护5个特定的,不同的JRE版本,我可能也对Java怀有偏见和偏见,其中一个原因可以追溯到2009年,原因是恶意软件的废除。 ...呃,我们用于关键业务应用程序的“企业级”软件套件。
HopelessN00b

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

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

这避免了KenD解决方案的CPU开销,该开销迫使OS n每次添加新级别,检查权限等时都将树从最高层遍历到第二层。因此,它具有sum(1, n) = n * (n-1) / 2 = O(n^2)时间复杂性。O(n)除非Windows在重命名其父目录时需要遍历一棵树,否则从链的开头删除大块的解决方案应该是。(Linux / Unix不需要。)chdir一直到树的底部并从那里使用相对路径,并在chdir备份时删除目录的解决方案也应该是O(n),前提是OS不需要检查所有当您在某处CD时执行操作时,parent目录会列出每个系统调用。

find Folder1 -depth -execdir rmdir {} +将CD放入最深目录时,它将运行rmdir。或实际上,find的-delete选项适用于目录,并暗含-depth。因此find Folder1 -delete应该做完全相同的事情,但是要更快。是的,在Linux上,GNU find会通过扫描目录,使用相对路径,然后rmdir使用相对路径,然后通过CD到子目录来下降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绿色电源驱动器(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 ops,真的为0.05秒。xfs非常擅长将日记中的元数据操作一起批处理,因为它们解决了元数据操作缓慢的问题,就像10年前一样。

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


好奇并在Linux上尝试过。事实证明,标准的GNU工具在长路径上都可以使用,因为它们沿树递归,而不是尝试使用巨大的长路径进行系统调用。当您在命令行中传递38k路径时,即使mkdir也可以!
彼得·科德斯

0

我确实遇到了与某些Java应用程序一样的5000+目录深文件夹混乱的问题,并且编写了一个程序来帮助您删除此文件夹。整个源代码在此链接中:

https://imanolbarba.net/gitlab/imanol/DiREKT

一段时间后,它消除了整个问题,但它成功地完成了工作,希望它对像我一样遇到同样令人沮丧的问题的人们有所帮助


请不要发布仅链接的答案。您应该将链接中最重要的信息放在帖子本身中,并提供链接以供参考。
Frederik Nielsen

哦,对不起,这是一个程序,我真的不想在这里发布所有源代码...我认为很明显,我编写了一个程序,并通过此链接托管它,动机和全部内容都在其中答案,所以对我来说,这显然不是一个仅链接的答案,不过,我将(更清楚地)指定它是旨在解决该问题的软件
Imanol Barba Sabariego

0

在独立的Windows 10系统上,我也有这个。C:\ User \ Name \ Repeat \ Repeat \ Repeat \ Repeat \ Repeat \ Repeat \ Repeat看似无穷大。

我可以使用Windows或命令提示符导航到第50个,然后再继续浏览。我无法删除它或单击它,等等。

C是我的语言,所以最终我写了一个带有系统调用循环的程序,重复执行直到失败。您可以用任何语言进行此操作,甚至可以使用DOS批处理。我创建了一个名为tmp的目录,并将Repeat \ Repeat移至该目录,删除了空的Repeat文件夹,然后将tmp \ Repeat移回了当前文件夹。一遍又一遍地!

 while (times<2000)
 {
  ChkSystem("move Repeat\\Repeat tmp");
  ChkSystem("rd Repeat");
  ChkSystem("move tmp\\Repeat Repeat");
  ++times;
  printf("Removed %d nested so far.\n", times);
 }

ChkSystem只是运行system()调用并检查返回值,如果失败则停止。

重要的是,它多次失败。我以为我的程序可能没有用,或者毕竟是无限长的时间。但是,我以前在系统调用中遇到过这种情况,事情没有同步,所以我只是再次运行该程序,它从中断处继续执行,所以不要立即认为您的程序无法正常工作。因此,总共运行了20次后,便全部清除了。总共总共大约有1280个文件夹。不知道是什么原因造成的。疯。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.