Usuwanie (najwyraźniej) nieskończenie rekurencyjnego folderu

46

W jakiś sposób jedno z naszych starych serwerów Server 2008 (nie R2) opracowało folder, który wydaje się nieskończenie cykliczny. To grozi spustoszeniem dzięki naszym kopiom zapasowym, ponieważ agent tworzenia kopii zapasowych próbuje powrócić do folderu i nigdy nie wraca.

Struktura folderów wygląda mniej więcej tak:

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

... i tak dalej. To jak jeden z tych zestawów Mandelbrot, z którymi wszyscy bawiliśmy się w latach 90-tych.

Próbowałem:

  • Usuwanie go z Eksploratora. Tak, jestem optymistą.
  • RMDIR C:\Storage\Folder1 /Q/S - to powraca The directory is not empty
  • ROBOCOPY C:\temp\EmptyDirectory C:\Storage\Folder1 /PURGE - powoduje to wirowanie folderów przez kilka minut przed awarią robocopy.exe.

Czy ktoś może zasugerować sposób na dobre zabicie tego folderu?

KenD
źródło
1
Spróbowałbym /MIRzamiast tego: ROBOCOPY /MIR C:\temp\EmptyDirectory C:\Storage\Folder1być może warto uruchomić chkdsktylko na chichoty.
jscott,
1
/MIRwydawało się, że trwa dłużej, ale ostatecznie też zbombardował („robocopy przestało działać”). Trochę boję się zrobić chkdsk; to dość stary serwer i martwię się, że ten problem wskazuje na większe problemy z systemem plików ...
KenD
7
Spróbuj uruchomić komputer z próbnej płyty CD z systemem Linux (Ubuntu / Centos / Fedora / ...) i usuń z niego folder.
Guntram Blohm obsługuje Monikę
2
@KenD Jeśli podejrzewasz problemy z uszkodzeniem systemu plików, z pewnością powinieneś najpierw spróbować naprawić system plików. Próbowanie sztuczek polegających na usuwaniu katalogów może pogorszyć sprawę.
jscott,
1
Ponieważ (z twojej odpowiedzi poniżej) katalog nie był nieskończony, tylko bardzo głęboki, jeśli miałeś zainstalowane CygWin lub UnxUtils , możesz użyć finddo pierwszego głębokiego usunięcia katalogu:find Storage/Folder1 -depth -exec rmdir {} \;
Johnny

Odpowiedzi:

45

Dziękujemy wszystkim za przydatną poradę.

Wchodząc dobrze na terytorium StackOverflow, rozwiązałem problem, wyrzucając ten fragment kodu C #. Korzysta z biblioteki Delimon.Win32.IO, która konkretnie rozwiązuje problemy z dostępem do długich ścieżek plików.

Na wypadek, gdyby to pomogło komuś innemu, oto kod - przeszedł przez ~ 1600 poziomów rekurencji, z którą jakoś utknąłem i zajęło mi około 20 minut, aby je wszystkie usunąć.

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--;
        }
    }
}
KenD
źródło
2
Próbowałem, nawet używając wersji Delimon .Delete(zamiast zwykłej System.IOwersji) i chociaż nie rzucało to wyjątku, nie wydawało się, że nic nie robi. Z pewnością rekursja przy użyciu powyższej metody trwała wieki i .Deleteżuł rzeczy tylko przez 5-10 sekund. Może odłamał się w kilku katalogach, a potem się poddał?
KenD,
4
Czy zdarzyło ci się kiedyś dowiedzieć, jak to się stało? Brzmi jak wina naprawdę źle napisanego programu użytkownika.
Parthian Shot
8
Wracasz do funkcji 1600 razy? Przepełnienie stosu terytorium rzeczywiście!
Aleksandr Dubinsky
2
Na marginesie, czy foldery były przez coś zapełniane? Jeśli potrafisz określić, w jakich odstępach czasu utworzono foldery rekurencyjne, i pomnożyć je przez liczbę rekurencji, (miejmy nadzieję) otrzymasz przybliżone ramy czasowe, kiedy ten problem się zaczął ...
Get-HomeByFiveOClock
8
Wow, cieszę się, że rozwiązałeś to. Do Twojej wiadomości, oficjalną obsługiwaną przez Microsoft poprawką tego rodzaju sytuacji jest „sformatowanie woluminu”. Poważnie. : /
HopelessN00b
25

Może być rekurencyjnym punktem połączenia. Taką rzecz można utworzyć za junctionpomocą narzędzia do obsługi plików i dysków Sysinternals .

mkdir c:\Hello
junction c:\Hello\Hello c:\Hello

I możesz teraz bez końca iść w dół c: \ Hello \ Hello \ Hello .... (aż do osiągnięcia MAX_PATH, 260 znaków dla większości poleceń, ale 32 767 znaków dla niektórych funkcji Windows API).

Lista katalogów pokazuje, że jest to skrzyżowanie:

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:\>

Aby usunąć, użyj narzędzia do łączenia:

junction -d c:\Hello\Hello
Brian
źródło
4
Niestety, DIRwłaśnie pokazuje mi zwykłe katalogi - nie boję się żadnych skrzyżowań - obawiam się
KenD
2
Czy możesz zrobić szybką podwójną kontrolę junction -s C:\Storage\Folder1 ?
Brian
3
No reparse points found:(
KenD
3
Zastanawiam się, co spowodowało taki bałagan w rzeczywistych podkatalogach.
Brian
2
Użyj, dir /aaby zobaczyć „<JUNCTION>” bez podawania konkretnej nazwy.
Chloe,
16

Brak odpowiedzi, ale nie mam wystarczającej liczby przedstawicieli do komentarza.

Kiedyś naprawiłem ten problem na ogromnej wówczas 500 MB płycie FAT16 w systemie MS-DOS. Użyłem debugowania DOS, aby ręcznie zrzucić i przeanalizować tabelę katalogów. Następnie odwróciłem jeden bit, aby zaznaczyć katalog rekurencyjny jako usunięty. Moja kopia Dettmana i Wyatta „DOS Programmers” Reference ”wskazała mi drogę.

Nadal jestem z tego niezwykle dumny. Byłbym zaskoczony i przerażony, gdyby istniało jakieś narzędzie ogólnego przeznaczenia, które ma taką moc nad woluminami FAT32 lub NTFS. Życie było wtedy prostsze.

Richard
źródło
8
Powiedziałbym, że są uzasadnione z tego dumni.
mfinni
3
+1 mają dla mnie jakieś powtórzenie. Niezłe rozwiązanie.
Chris Thornton
3
Możesz teraz usunąć pierwsze zdanie swojej odpowiedzi.
AL
To nie jest odpowiedź. To historia o innym systemie operacyjnym i innym systemie plików. Oprócz tego, że nie jest pomocny dla tego problemu (NT, NTFS), nie pomógłby nawet komuś z tym samym problemem (DOS, FAT16), ponieważ tak naprawdę nie zawiera żadnych szczegółów.
Andrew Medico
@Andrew Medico: Zgadzam się z tobą, stąd moje pierwsze zdanie. Ale mówię ci, gdzie znaleźć informacje, aby rozwiązać nieco powiązany problem.
Richard
8

Java może również radzić sobie z długimi ścieżkami plików. I może to zrobić znacznie szybciej. Ten kod (który skopiowałem z dokumentacji Java API) usunie strukturę katalogu głębokiego na poziomie 1600 w około 1 sekundę (pod Windows 7, Java 8.0) i bez ryzyka przepełnienia stosu, ponieważ tak naprawdę nie używa rekurencji.

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"));
  }
}
Pająk świnia
źródło
3
Nienawidzę cię. Zmusiłeś mnie do głosowania za odpowiedzią dotyczącą Java, a teraz czuję się brudny. Potrzebuję prysznica
HopelessN00b
Przepraszam. Mam nadzieję, że w końcu dojdziesz do siebie po traumie. Ale czy język opracowany przez Microsoft (c #) jest naprawdę znacznie lepszy?
SpiderPig
5
Obecnie jestem przede wszystkim facetem z Windows, więc tak, tak jest. Mogę być również zgorzkniały i stronniczy w stosunku do Javy z powodu konieczności utrzymywania 5 specyficznych, różnych wersji JRE na prawie 1000 klientów w środowisku mojego pracodawcy, z których jedna pochodzi z 2009 roku, z powodu bzdurnego ... złośliwego oprogramowania ... uh, "oprogramowanie dla przedsiębiorstw", których używamy do krytycznych aplikacji biznesowych.
HopelessN00b
6

Nie potrzebujesz długich nazw ścieżek, jeśli wchodzisz chdirdo katalogu i po prostu używasz ścieżek względnych do rmdir.

Lub, jeśli masz zainstalowaną powłokę POSIX lub przenieś ją do odpowiednika 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

(Użycie zmiennej powłoki do śledzenia miejsca, w którym została zmieniona jej nazwa dla warunku pętli, jest inną alternatywą dla rozwijania pętli, jak tam.)

Pozwala to uniknąć obciążenia procesora przez rozwiązanie KenD, które zmusza system operacyjny do przechodzenia przez drzewo od góry do npoziomu th za każdym razem, gdy dodawany jest nowy poziom, sprawdzania uprawnień itp. Ma więc sum(1, n) = n * (n-1) / 2 = O(n^2)złożoność czasową. Powinny istnieć rozwiązania, które oddzielają fragment od początku łańcucha O(n), chyba że system Windows musi przejść przez drzewo podczas zmiany nazwy katalogu nadrzędnego. (Linux / Unix nie.) Rozwiązania, chdirktóre chdirsięgają do samego końca drzewa i używają stamtąd ścieżek względnych, usuwając katalogi podczas tworzenia kopii zapasowej, powinny również być O(n), zakładając, że system operacyjny nie musi sprawdzać wszystkich katalogi nadrzędne każde wywołanie systemowe, gdy robisz coś, gdy gdzieś jest CD.

find Folder1 -depth -execdir rmdir {} +uruchomi rmdir podczas zapisywania CD do najgłębszego katalogu. A właściwie -deleteopcja find działa na katalogach i implikuje -depth. find Folder1 -deletePowinien więc zrobić dokładnie to samo, ale szybciej. Tak, GNU find w Linuksie schodzi przez skanowanie katalogu, CDing do podkatalogów ze ścieżkami względnymi, a następnie rmdirścieżką względną chdir(".."). Nie skanuje ponownie katalogów podczas wstępowania, więc zużywa O(n)pamięć RAM.

To było naprawdę przybliżenie: stracepokazy faktycznie korzysta unlinkat(AT_FDCWD, "tmp", AT_REMOVEDIR), open("..", O_DIRECTORY|...)oraz fchdir(the fd from opening the directory)z grupą fstatpołączeń mieszane, też. Ale efekt jest taki sam, jeśli drzewo katalogów nie jest modyfikowane podczas działania funkcji find.

edycja: Tylko dla kopnięć próbowałem tego na GNU / Linux (Ubuntu 14.10, na pierwszej generacji procesorze Core2Duo pierwszej generacji 2,4 GHz, na systemie plików XFS na dysku WD 2.5TB Green Power (WD25EZRS)).

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 tworzy katalog i wszelkie brakujące elementy ścieżki).

Tak, naprawdę 0,05 sekundy na 2k rmdir ops. xfs jest całkiem dobry w grupowaniu operacji na metadanych w czasopiśmie, ponieważ naprawiono powolne operacje metadanych, jak 10 lat temu.

Na ext4 tworzenie zajęło 0m0,279s, usunięcie z szukaniem wciąż zajęło 0m0,074s.

Peter Cordes
źródło
zainteresowałem się i wypróbowałem to w systemie Linux. Okazuje się, że standardowe narzędzia GNU są w porządku z długimi ścieżkami, ponieważ powtarzają się w dół drzewa zamiast próbować wywoływać systemowe z gigantycznymi długimi ścieżkami. Nawet mkdir jest w porządku, jeśli podasz mu ścieżkę 38k w linii poleceń!
Peter Cordes
0

Zetknąłem się z tym samym problemem związanym z katalogiem o głębokości katalogów ponad 5000, co niektóre aplikacje Java, i napisałem program, który pomoże ci usunąć ten folder. Cały kod źródłowy znajduje się w tym linku:

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

Po pewnym czasie to wszystko usunęło, ale udało się to zrobić, mam nadzieję, że pomoże ludziom, którzy (tak jak ja) napotykają ten sam frustrujący problem

Imanol Barba Sabariego
źródło
Nie publikuj odpowiedzi zawierających tylko linki. Najważniejsze informacje z linku należy umieścić w samym poście i podać link w celach informacyjnych.
Frederik Nielsen
Och, przepraszam, to program i naprawdę nie chciałem tutaj publikować CAŁEGO kodu źródłowego ... Myślę, że jest całkiem jasne , że napisałem program i hostuję go po tym linku, motywacja i wszystko jest w porządku odpowiedź, więc wydaje mi się dość jasna, że ​​nie jest to odpowiedź tylko do łącza, niemniej jednak sprecyzuję (jaśniej), że jest to oprogramowanie przeznaczone do uruchomienia w celu rozwiązania tego problemu
Imanol Barba Sabariego
0

Ja też to miałem, w samodzielnym systemie Windows 10. C: \ Użytkownik \ Nazwa \ Powtórz \ Powtórz \ Powtórz \ Powtórz \ Powtórz \ Powtórz \ Powtórz pozornie do nieskończoności.

Mógłbym nawigować za pomocą Windowsa lub wiersza polecenia do około 50. i nie dalej. Nie mogłem go usunąć ani kliknąć, itp.

C jest moim językiem, więc w końcu napisałem program z pętlą wywołań systemowych, które powtarzają się aż do niepowodzenia. Możesz to jednak zrobić w dowolnym języku, nawet partię DOS. Zrobiłem katalog o nazwie tmp i przeniosłem do niego Repeat \ Repeat, usunąłem teraz pusty folder Repeat i przeniosłem tmp \ Repeat z powrotem do bieżącego folderu. W kółko!

 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 po prostu uruchamia wywołanie system () i sprawdza wartość zwracaną, zatrzymując się, jeśli się nie powiedzie.

Co ważne, zawiodło wiele razy. Myślałem, że może mój program nie działa lub że mimo wszystko był nieskończenie długi. Jednak miałem to już wcześniej przy wywołaniach systemowych, przy czym rzeczy się nie synchronizowały, więc po prostu ponownie uruchomiłem program i kontynuowałem od miejsca, w którym został przerwany, więc nie myśl od razu, że twój program nie działa. W sumie, po uruchomieniu go około 20 razy, wyczyścił je wszystkie. W sumie pierwotnie miało około 1280 folderów. Nie mam pojęcia, co to spowodowało. Zwariowany.

DenM
źródło