Entfernen eines (scheinbar) unendlich rekursiven Ordners

46

Irgendwie hat eine unserer alten Server 2008-Boxen (nicht R2) einen scheinbar unendlich rekursiven Ordner entwickelt. Dies spielt bei unseren Backups eine große Rolle, da der Backup-Agent versucht, in den Ordner zurückzukehren, und niemals zurückkehrt.

Die Ordnerstruktur sieht ungefähr so ​​aus:

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

... und so weiter. Es ist wie eines dieser Mandelbrot-Sets, mit denen wir alle in den 90ern gespielt haben.

Ich habe es versucht:

  • Löschen Sie es aus dem Explorer. Ja, ich bin ein Optimist.
  • RMDIR C:\Storage\Folder1 /Q/S - das kehrt zurück The directory is not empty
  • ROBOCOPY C:\temp\EmptyDirectory C:\Storage\Folder1 /PURGE - Dies dreht sich einige Minuten lang durch die Ordner, bevor robocopy.exe abstürzt.

Kann jemand einen Weg vorschlagen, um diesen Ordner endgültig zu löschen?

KenD
quelle
1
Ich würde es /MIRstattdessen versuchen : ROBOCOPY /MIR C:\temp\EmptyDirectory C:\Storage\Folder1Vielleicht lohnt es sich auch, chkdsknur für ein Kichern zu laufen .
Jscott
1
/MIRschien länger zu dauern, wurde aber schließlich auch bombardiert ("Robocopy hat aufgehört zu funktionieren"). Ich habe ein bisschen Angst ein zu tun chkdsk; Dies ist ein ziemlich alter Server und ich mache mir Sorgen, dass dieses Problem auf größere Dateisystemprobleme hinweist ...
KenD
7
Versuchen Sie, von einer Linux-Desktop-Test-CD (Ubuntu / Centos / Fedora / ...) zu booten und den Ordner von dort zu entfernen.
Guntram Blohm unterstützt Monica
2
@KenD Wenn Sie den Verdacht haben, dass das Dateisystem beschädigt ist, sollten Sie unbedingt zuerst versuchen, das Dateisystem zu reparieren. Das Ausprobieren von Tricks zum Entfernen von Verzeichnissen kann die Situation verschlimmern.
Jscott
1
Da das Verzeichnis (aus Ihrer Antwort unten) nicht unendlich war, nur sehr tief, wenn Sie CygWin oder UnxUtils installiert hatten, konnten findSie eine erste Tiefenentfernung des Verzeichnisses durchführen:find Storage/Folder1 -depth -exec rmdir {} \;
Johnny

Antworten:

45

Vielen Dank an alle für den nützlichen Rat.

Auf dem Weg nach StackOverflow habe ich das Problem gelöst, indem ich dieses C # -Code-Snippet in die Knie gezwungen habe. Es verwendet die Delimon.Win32.IO- Bibliothek, die speziell Probleme beim Zugriff auf lange Dateipfade behebt .

Nur für den Fall, dass dies jemand anderem helfen kann, hier ist der Code - er hat die ~ 1600 Rekursionsebenen durchlaufen, mit denen ich irgendwie festgefahren war, und es dauerte ungefähr 20 Minuten, um sie alle zu entfernen.

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
quelle
2
Ich habe versucht, auch mit der Delimon-Version von .Delete(anstelle der normalen System.IOVersion), und obwohl es keine Ausnahme warf, schien es nichts zu tun. Sicherlich dauerte die Rekursion mit der oben beschriebenen Methode eine Ewigkeit und dauerte .Deletenur 5-10 Sekunden. Vielleicht hat es ein paar Verzeichnisse weggeschlagen und dann aufgegeben?
KenD,
4
Hast du jemals herausgefunden, wie das angefangen hat? Klingt nach einem wirklich schlecht geschriebenen Userland-Programm.
Parthian Shot
8
1600 mal in eine Funktion rekursieren? Stapelüberlaufgebiet in der Tat!
Aleksandr Dubinsky
2
Abgesehen davon, wurden die Ordner von irgendetwas bevölkert? Wenn Sie ermitteln können, in welchen Intervallen die rekursiven Ordner erstellt wurden, und diese mit der Anzahl der Rekursionen multiplizieren, erhalten Sie (hoffentlich) einen ungefähren Zeitrahmen für den Beginn dieses Problems ...
Get-HomeByFiveOClock
8
Wow, ich bin froh, dass du das gelöst hast. Zu Ihrer Information: Die von Microsoft offiziell unterstützte Lösung für diese Art von Situation lautet "Formatieren Sie das Volume neu." Ja, im Ernst. : /
HopelessN00b
25

Könnte ein rekursiver Knotenpunkt sein. So etwas kann mit junctioneinem Datei- und Festplatten-Dienstprogramm von Sysinternals erstellt werden .

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

Und jetzt können Sie endlos nach unten gehen: c: \ Hello \ Hello \ Hello .... (nun, bis MAX_PATH erreicht ist, 260 Zeichen für die meisten Befehle, aber 32.767 Zeichen für einige Windows-API-Funktionen).

Eine Verzeichnisliste zeigt, dass es sich um eine Junction handelt:

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

Verwenden Sie zum Löschen das Junction-Dienstprogramm:

junction -d c:\Hello\Hello
Brian
quelle
4
Leider DIRzeigt mir eine nur einfache alte Verzeichnisse - keine Anzeichen von Kreuzungen fürchte ich
KenD
2
Können Sie eine schnelle Überprüfung mit machen junction -s C:\Storage\Folder1 ?
Brian,
3
No reparse points found:(
KenD
3
Ich frage mich, was so ein Durcheinander von tatsächlichen Unterverzeichnissen geschaffen hat.
Brian
2
Verwenden Sie dir /a, um '<JUNCTION>' anzuzeigen, ohne den spezifischen Namen anzugeben.
Chloe
16

Keine Antwort, aber ich habe nicht genug Repräsentanten für einen Kommentar.

Ich habe dieses Problem einmal auf einer damals riesigen 500-MB-FAT16-Disk auf einem MS-DOS-System behoben. Ich habe DOS-Debug verwendet, um die Verzeichnistabelle manuell auszugeben und zu analysieren. Ich habe dann ein Bit umgedreht, um das rekursive Verzeichnis als gelöscht zu markieren. Mein Exemplar von Dettman und Wyatt 'DOS Programmers' Reference 'zeigte mir den Weg.

Darauf bin ich immer noch außerordentlich stolz. Ich wäre erstaunt und verängstigt, wenn es ein Allzweck-Tool geben würde, das über eine solche Leistung für FAT32- oder NTFS-Volumes verfügt. Das Leben war damals einfacher.

Richard
quelle
8
Ich würde sagen, dass Sie zu Recht stolz darauf sind.
Mfinni
3
+1 Ich habe einen Repräsentanten. Gute Lösung.
Chris Thornton
3
Sie können jetzt den ersten Satz Ihrer Antwort löschen.
AL
Dies ist keine Antwort. Es ist eine Geschichte über ein anderes Betriebssystem und ein anderes Dateisystem. Dieses Problem (NT, NTFS) hilft nicht nur nichts, es hilft auch niemandem mit demselben Problem (DOS, FAT16), da es keine Details enthält.
Andrew Medico
@ Andrew Medico: Ich stimme Ihnen zu, daher mein erster Satz. Aber ich sage Ihnen, wo Sie die Informationen finden, um das leicht verwandte Problem zu lösen.
Richard
8

Java kann auch mit langen Dateipfaden umgehen. Und es geht auch viel schneller. Dieser Code (den ich aus der Java-API-Dokumentation kopiert habe) löscht eine Verzeichnisstruktur mit 1600 Ebenen in ungefähr 1 Sekunde (unter Windows 7, Java 8.0) und ohne das Risiko eines Stapelüberlaufs, da keine Rekursion verwendet wird.

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"));
  }
}
Spinnen Schwein
quelle
3
Ich hasse dich. Sie haben mich gezwungen, eine Antwort zu stimmen, die die Verwendung von Java beinhaltet, und jetzt fühle ich mich alles schmutzig. Ich brauche eine Dusche.
HopelessN00b
Entschuldigen Sie. Ich hoffe, Sie werden sich irgendwann von Ihrem Trauma erholen. Aber ist eine von Microsoft (c #) entwickelte Sprache wirklich viel besser?
SpiderPig
5
Ich bin heutzutage hauptsächlich ein Windows-Typ, also ja, ja, das ist es. Möglicherweise bin ich auch bitter und voreingenommen gegenüber Java, da ich in der Umgebung meines Arbeitgebers 5 verschiedene JRE-Versionen für fast 1.000 Kunden verwalten muss. Eine davon stammt aus dem Jahr 2009, da es sich um Schadsoftware handelt ... äh, "Enterprise-y" -Software-Suiten, die wir für geschäftskritische Anwendungen verwenden.
HopelessN00b
6

Sie benötigen keine langen Pfadnamen, wenn Sie chdirsich im Verzeichnis befinden und nur relative Pfade zu verwenden rmdir.

Oder wenn Sie eine POSIX-Shell installiert haben oder diese auf das DOS-Äquivalent portieren:

# 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

(Die Verwendung einer Shell-Variablen zum Verfolgen, wo Sie sie für die Schleifenbedingung umbenannt haben, ist die andere Alternative zum Aufrollen der Schleife, wie ich es dort getan habe.)

Dies vermeidet den CPU-Overhead der KenD-Lösung, der das Betriebssystem dazu zwingt, den Baum njedes Mal von oben nach unten zu durchlaufen, wenn eine neue Ebene hinzugefügt wird, Berechtigungen überprüft usw. Das hat also sum(1, n) = n * (n-1) / 2 = O(n^2)zeitliche Komplexität. Lösungen, die von Anfang an einen Teil der Kette abtrennen O(n), sollten es sein , es sei denn, Windows muss beim Umbenennen des übergeordneten Verzeichnisses einen Baum durchlaufen. (Linux / Unix nicht.) Lösungen, chdirdie sich bis zum Ende des Baums erstrecken und von dort aus relative Pfade verwenden und Verzeichnisse beim Sichern entfernen chdir, sollten auch O(n)funktionieren, vorausgesetzt, das Betriebssystem muss nicht alle Ihre Pfade überprüfen Übergeordnete Verzeichnisse bei jedem Systemaufruf, wenn Sie Dinge erledigen, während Sie sich auf einer CD befinden.

find Folder1 -depth -execdir rmdir {} +wird rmdir ausführen, während die CD in das tiefste Verzeichnis verschoben wird. Oder tatsächlich -deletefunktioniert finds Option für Verzeichnisse und impliziert -depth. Also find Folder1 -deletesollte genau das Gleiche tun, aber schneller. Ja, GNU find unter Linux wird durch Durchsuchen eines Verzeichnisses ausgeführt und in Unterverzeichnisse mit relativen Pfaden und dann rmdirmit relativen Pfaden kopiert chdir(".."). Während des Aufstiegs werden keine Verzeichnisse erneut durchsucht, sodass O(n)RAM verbraucht wird .

Das war wirklich eine Annäherung: stracezeigt, dass es WIRKLICH verwendet unlinkat(AT_FDCWD, "tmp", AT_REMOVEDIR)wird open("..", O_DIRECTORY|...), und fchdir(the fd from opening the directory)mit ein paar fstatGesprächen auch. Der Effekt ist jedoch der gleiche, wenn der Verzeichnisbaum während der Ausführung von find nicht geändert wird.

Bearbeiten: Nur zum Spaß habe ich dies unter GNU / Linux versucht (Ubuntu 14.10, auf einer Core2Duo-CPU der ersten Generation mit 2,4 GHz, auf einem XFS-Dateisystem auf einem WD 2,5 TB Green Power-Laufwerk (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 erstellt ein Verzeichnis und alle fehlenden Pfadkomponenten).

Ja, wirklich 0,05 Sekunden für 2k rmdir ops. xfs ist ziemlich gut darin, Metadatenoperationen im Journal zusammenzufassen, da es feststellte, dass Metadatenoperationen wie vor 10 Jahren langsam waren.

Auf ext4 hat das Erstellen 0m0.279s gedauert, das Löschen mit find hat noch 0m0.074s gedauert.

Peter Cordes
quelle
wurde neugierig und versuchte es unter Linux. Es hat sich herausgestellt, dass die Standard-GNU-Tools mit langen Pfaden alle in Ordnung sind, da sie den Baum zurückverfolgen, anstatt zu versuchen, Systemaufrufe mit riesigen langen Pfaden durchzuführen. Sogar mkdir ist in Ordnung, wenn Sie in der Befehlszeile einen 38k-Pfad übergeben!
Peter Cordes
0

Ich bin auf dasselbe Problem gestoßen, bei dem es um mehr als 5000 verzeichnisintensive Ordner ging, wie bei einigen Java-Anwendungen, und ich habe ein Programm geschrieben, mit dem Sie diesen Ordner entfernen können. Der gesamte Quellcode befindet sich in diesem Link:

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

Es hat das Ganze nach einer Weile entfernt, aber es hat es geschafft, die Arbeit zu erledigen. Ich hoffe, es hilft Menschen, die (wie ich) auf dasselbe frustrierende Problem stoßen

Imanol Barba Sabariego
quelle
Bitte poste keine Nur-Link-Antworten. Sie sollten die wichtigsten Informationen aus dem Link in den Beitrag selbst einfügen und den Link als Referenz bereitstellen.
Frederik Nielsen
Oh, tut mir leid, es ist ein Programm, und ich wollte wirklich nicht ALLEN Quellcode hier veröffentlichen ... Ich denke, es ist ziemlich klar, dass ich ein Programm geschrieben habe und es über diesen Link hoste, und die Motivation und alles ist drin Die Antwort, so dass es für mich ziemlich klar aussieht, ist keine reine Link-Antwort. Trotzdem werde ich (klarer) angeben, dass es sich um eine Software handelt, die zur Lösung dieses Problems ausgeführt werden soll
Imanol Barba Sabariego,
0

Ich hatte dies auch auf einem eigenständigen Windows 10-System. C: \ Benutzer \ Name \ Wiederholen \ Wiederholen \ Wiederholen \ Wiederholen \ Wiederholen \ Wiederholen \ Wiederholen scheinbar unendlich.

Ich konnte mit Windows oder der Eingabeaufforderung zum 50. navigieren und nicht weiter. Ich konnte es nicht löschen oder darauf klicken usw.

C ist meine Sprache, also habe ich schließlich ein Programm mit einer Schleife von Systemaufrufen geschrieben, die sich wiederholen, bis sie fehlschlagen. Sie können dies jedoch in jeder Sprache tun, auch im DOS-Batch. Ich habe ein Verzeichnis mit dem Namen tmp erstellt und Repeat \ Repeat in dieses Verzeichnis verschoben, den jetzt leeren Repeat-Ordner gelöscht und tmp \ Repeat wieder in den aktuellen Ordner verschoben. Wieder und wieder!

 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 führt nur einen system () -Aufruf aus und überprüft den Rückgabewert.

Es ist wichtig, dass es einige Male fehlgeschlagen ist. Ich dachte, mein Programm würde vielleicht nicht funktionieren oder es wäre unendlich lange her. Ich hatte dies jedoch schon einmal mit Systemaufrufen, bei denen die Dinge nicht synchronisiert wurden. Deshalb habe ich das Programm erneut ausgeführt und es wurde an der Stelle fortgesetzt, an der es aufgehört hat. Denken Sie also nicht sofort, dass Ihr Programm nicht funktioniert. Insgesamt löschte es sie alle, nachdem es ungefähr 20 Mal ausgeführt worden war. Insgesamt waren es ursprünglich ungefähr 1280 Ordner. Keine Ahnung woran es liegt. Verrückt.

DenM
quelle