무한 재귀 폴더 제거


46

어쨌든, 우리의 오래된 Server 2008 (R2 아님) 상자 중 하나는 무한히 반복되는 폴더를 개발했습니다. 백업 에이전트가 폴더로 재귀를 시도하고 다시는 돌아 오지 않기 때문에 백업과 함께 작동합니다.

폴더 구조는 다음과 같습니다.

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

... 등등. 그것은 90 년대에 우리 모두가 함께 사용 했던 Mandelbrot 세트 중 하나와 같습니다 .

난 노력 했어:

  • 탐색기에서 삭제합니다. 네, 저는 낙천주의 자입니다.
  • RMDIR C:\Storage\Folder1 /Q/S -이 반환 The directory is not empty
  • ROBOCOPY C:\temp\EmptyDirectory C:\Storage\Folder1 /PURGE -이것은 robocopy.exe가 충돌하기 전에 몇 분 동안 폴더를 회전합니다.

누구나이 폴더를 잘 제거하는 방법을 제안 할 수 있습니까?


1
/MIR대신 시도해보십시오 : ROBOCOPY /MIR C:\temp\EmptyDirectory C:\Storage\Folder1또한 chkdsk낄낄 거림을 실행할 가치가 있습니다 .
jscott

1
/MIR더 오래 지속되는 것처럼 보였지만 결국 폭탄이 터져 나갔습니다 ( "로보 카피가 작동을 멈췄습니다"). 나는 조금 무서워 chkdsk; 이것은 꽤 오래된 서버
이며이

7
Linux (Ubuntu / Centos / Fedora / ...) 데스크탑 평가판 CD로 부팅하고 폴더를 제거하십시오.
Guntram Blohm은

2
@KenD 파일 시스템 손상 문제가 의심되면 파일 시스템을 먼저 복구해야합니다. 디렉토리 제거 트릭을 시도하면 상황이 악화 될 수 있습니다.
jscott

1
CygWin 또는 UnxUtils를 설치 한 경우 디렉토리가 무한하지 않고 매우 깊지 않기 때문에 디렉토리 find의 깊이를 먼저 제거 할 수 있습니다 .find Storage/Folder1 -depth -exec rmdir {} \;
Johnny

답변:


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버전 대신)을 사용하여 시도했지만 예외는 발생하지 않았지만 아무것도하지 않는 것 같습니다. 분명히 위의 방법을 사용한 재귀는 오래 걸렸으며 .Delete5-10 초 동안 물건을 씹었습니다. 어쩌면 그것은 몇 개의 디렉토리에서 부서져서 포기했을까요?
KenD

4
어떻게 시작했는지 알아 본 적이 있습니까? 실제로 잘못 작성된 userland 프로그램의 오류처럼 들립니다.
Parthian Shot

8
1600 번 함수로 되풀이? 실제로 오버플로 영역을 쌓아 라!
Aleksandr Dubinsky

2
옆으로 폴더가 채워져 있습니까? 재귀 폴더가 생성 된 간격을 결정하고 재귀 수를 곱하면이 문제가 시작된 대략적인 시간 범위를 갖게됩니다.
Get-HomeByFiveOClock

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 유틸리티를 삭제하려면 다음을 수행하십시오.

junction -d c:\Hello\Hello

4
불행히도, DIR그냥 나에게 일반 ole 디렉토리를 보여줍니다-내가 두려워하는 접합부의 흔적이 없습니다.
KenD

2
빠른 재확인을 할 수 있습니까 junction -s C:\Storage\Folder1 ?
Brian

3
No reparse points found:(
KenD

3
실제 하위 디렉토리가 어떻게 엉망인지 궁금합니다.
브라이언

2
dir /a특정 이름을 지정하지 않고 '<JUNCTION>'을 보는 데 사용 하십시오.
Chloe

16

답이 아니지만 의견을 제출할 충분한 담당자가 없습니다.

한 번 MS-DOS 시스템의 거대한 500MB FAT16 디스크에서이 문제를 해결했습니다. DOS 디버그를 사용하여 디렉토리 테이블을 수동으로 덤프하고 구문 분석했습니다. 그런 다음 재귀 디렉토리를 삭제 된 것으로 표시하기 위해 1 비트를 뒤집 었습니다. Dettman과 Wyatt 'DOS Programmers'Reference '의 사본은 저에게 길을 보여주었습니다.

나는 여전히 이것을 자랑스럽게 생각합니다. FAT32 또는 NTFS 볼륨보다 강력한 기능을 갖춘 범용 도구가 있다면 놀랍습니다. 그 당시의 삶은 더 단순했습니다.


8
나는 당신이 말할 것 정당 이 자랑.
mfinni

3
+1 응답이 있습니다. 좋은 해결책.
Chris Thornton

3
이제 답변의 첫 문장을 삭제할 수 있습니다.
AL

이것은 답이 아닙니다. 다른 운영 체제와 다른 파일 시스템에 대한 이야기입니다. 이 문제 (NT, NTFS)에 대한 도움이되지 않는 것 외에도 실제로는 세부 사항이 포함되어 있지 않기 때문에 같은 문제 (DOS, FAT16)를 가진 사람에게는 도움이되지 않습니다.
Andrew Medico

@Andrew Medico : 동의합니다. 따라서 첫 번째 문장입니다. 그러나 약간 관련된 문제를 해결할 정보를 어디에서 찾을 수 있는지 알려줍니다.
Richard

8

Java는 긴 파일 경로를 처리 할 수도 있습니다. 그리고 훨씬 더 빨리 할 수 ​​있습니다. 이 코드 (Java API 문서에서 복사 한)는 약 1 초 (Windows 7, Java 8.0에서)에서 1600 수준의 딥 디렉토리 구조를 삭제하며 실제로 재귀를 사용하지 않기 때문에 스택 오버플로의 위험이 없습니다.

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 2019

사과드립니다. 결국 당신의 외상에서 회복되기를 바랍니다. 그러나 Microsoft (c #)에서 개발 한 언어가 훨씬 더 좋습니까?
SpiderPig 2019

5
나는 요즘에는 주로 Windows 사람이므로 예, 그렇습니다. 또한 고용주 환경에서 거의 1,000 명의 클라이언트에 대해 5 개의 특정 버전의 JRE를 유지해야한다는 이유로 Java에 대해 괴로움과 편견이있을 수 있습니다. ... 비즈니스 핵심 응용 프로그램에 사용하는 "엔터프라이즈 -y"소프트웨어 제품군입니다.
HopelessN00b 2019

6

chdir디렉토리에 들어가고 상대 경로를 사용하는 경우 긴 경로 이름이 필요하지 않습니다 rmdir.

또는 POSIX 셸이 설치되어 있거나 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

루프 조건에서 쉘 변수를 사용하여 이름을 바꾼 위치를 추적하는 것은 내가했던 것처럼 루프를 풀 때의 다른 대안입니다.

이렇게하면 KenD 솔루션의 CPU 오버 헤드를 피할 수 있습니다. 이로 인해 OS n는 새 수준이 추가 될 때마다 트리를 최상위에서 최상위 수준으로 이동하고 권한 등을 확인하므로 sum(1, n) = n * (n-1) / 2 = O(n^2)시간이 복잡합니다. 체인의 시작 부분에서 청크를 제거하는 솔루션 O(n)은 부모 디렉토리의 이름을 바꿀 때 Windows가 트리를 통과 해야하는 경우 가 아니면 안됩니다 . (Linux / Unix는 그렇지 않습니다.) OS가 모든 것을 검사 할 필요가 없다고 가정 할 때 chdir트리의 맨 아래까지 내려 가면서 경로를 chdir백업하고 디렉토리를 제거하는 솔루션 도 있어야 O(n)합니다. CD가있는 동안 작업을 수행 할 때 모든 시스템 호출마다 상위 디렉토리.

find Folder1 -depth -execdir rmdir {} +CD가 가장 깊은 디렉토리에있는 동안 rmdir을 실행합니다. 또는 실제로 find의 -delete옵션은 디렉토리에서 작동하며을 의미합니다 -depth. 그래서 find Folder1 -delete똑같은 일을하지만, 빨리해야한다. 그래, 리눅스에서 GNU의 발견은 다음 상대 경로와 하위 디렉토리에있는 디렉토리, 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 1 세대 Core2Duo CPU, WD 2.5TB Green Power drive (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가되었습니다.


궁금해서 리눅스에서 사용해 보았습니다. 표준 GNU 도구는 긴 경로를 사용하는 것이 좋습니다. 거대한 긴 경로를 사용하여 시스템 호출을 시도하는 대신 트리를 다시 사용하기 때문입니다. mkdir조차도 명령 행에서 38k 경로를 전달하면 좋습니다!
Peter Cordes

0

일부 Java 응용 프로그램에서했던 5000+ 디렉토리 깊이의 폴더 엉망과 같은 문제가 발생 했으며이 폴더를 제거하는 데 도움이되는 프로그램을 작성했습니다. 전체 소스 코드는 다음 링크에 있습니다.

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

그것은 잠시 후에 모든 것을 제거했지만 일을 처리했습니다. 나는 그것이 같은 좌절감을 느끼는 사람들에게 도움이되기를 바랍니다.


링크 전용 답변을 게시하지 마십시오. 링크 자체에서 가장 중요한 정보를 게시물 자체에 넣고 참조 할 링크를 제공해야합니다.
프레데릭 닐슨

죄송합니다. 프로그램입니다. 모든 소스 코드를 여기에 게시하고 싶지 않았습니다 ... 프로그램을 작성하고이 링크를 따라 프로그램을 호스팅하고 있다는 것이 확실 합니다. 대답, 그것은 나에게 매우 명확 보이는 있도록이 아니 링크 전용 대답은, nontheless는, 내가이 문제를 해결하기 위해 실행하는 의미 소프트웨어입니다 (더 명확하게)를 지정할 수 있습니다
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 개였습니다. 그 원인을 알 수 없습니다. 미친.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.