Rimozione di una cartella (apparentemente) infinitamente ricorsiva


46

In qualche modo, una delle nostre vecchie scatole Server 2008 (non R2) ha sviluppato una cartella apparentemente infinitamente ricorrente. Questo sta giocando a havock con i nostri backup, poiché l'agente di backup tenta di rientrare nella cartella e non ritorna mai.

La struttura delle cartelle è simile a:

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

... e così via. È come uno di quei set di Mandelbrot con cui giocavamo tutti negli anni '90.

Ho provato:

  • Eliminandolo da Explorer. Sì, sono un ottimista.
  • RMDIR C:\Storage\Folder1 /Q/S - questo ritorna The directory is not empty
  • ROBOCOPY C:\temp\EmptyDirectory C:\Storage\Folder1 /PURGE - questo scorre tra le cartelle per un paio di minuti prima che si blocchi il file robocopy.exe.

Qualcuno può suggerire un modo per eliminare definitivamente questa cartella?


1
Proverei /MIRinvece: ROBOCOPY /MIR C:\temp\EmptyDirectory C:\Storage\Folder1potrebbe anche valere la pena correre chkdsksolo per ridacchiare.
jscott,

1
/MIRsembrava durare più a lungo, ma alla fine anche bombardato ("il robocopy ha smesso di funzionare"). Ho un po 'paura di fare un chkdsk; questo è un server piuttosto vecchio e sono preoccupato che questo problema sia indicativo di maggiori problemi del file system ...
KenD,

7
Prova a eseguire l'avvio da un CD di prova desktop Linux (Ubuntu / Centos / Fedora / ...) e rimuovi la cartella da lì.
Guntram Blohm supporta Monica il

2
@KenD Se sospetti problemi di corruzione del file system, dovresti certamente provare a riparare prima il file system. I trucchi di rimozione delle directory potrebbero peggiorare le cose.
jscott,

1
Poiché (dalla tua risposta di seguito), la directory non era infinita, solo molto profonda, se avevi installato CygWin o UnxUtils , puoi usare findper eseguire una prima rimozione approfondita della directory:find Storage/Folder1 -depth -exec rmdir {} \;
Johnny,

Risposte:


45

Grazie a tutti per il consiglio utile.

Allontanandomi nel territorio di StackOverflow, ho risolto il problema eliminando questo frammento di codice C #. Utilizza la libreria Delimon.Win32.IO che affronta in modo specifico i problemi di accesso a percorsi di file lunghi.

Nel caso in cui questo possa aiutare qualcun altro, ecco il codice: ha superato i ~ 1600 livelli di ricorsione in cui in qualche modo sono rimasto bloccato e ci sono voluti circa 20 minuti per rimuoverli tutti.

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
Ho provato, anche usando la versione di Delimon .Delete(invece della System.IOversione normale ), e sebbene non abbia fatto eccezione, non sembra aver fatto nulla. Certamente la ricorsione usando il metodo sopra ha richiesto secoli e ha .Deletemasticato le cose solo per 5-10 secondi. Forse è stato eliminato in alcune directory e poi ha rinunciato?
KenD,

4
Hai mai capito come è successo per cominciare? Sembra un errore di un programma utente davvero scritto male.
Parthian Shot il

8
Ricorrere in una funzione 1600 volte? Impila davvero il territorio di overflow !
Aleksandr Dubinsky,

2
A parte questo, le cartelle erano popolate da qualcosa? Se riesci a determinare a quali intervalli sono state create le cartelle ricorsive e moltiplicalo per il numero di ricorsioni, otterrai (si spera) un periodo di tempo approssimativo di quando è iniziato questo problema ...
Get-HomeByFiveOClock

8
Wow, felice di aver finito per risolvere questo. Cordiali saluti, la correzione ufficiale supportata da Microsoft a questo tipo di situazione è "riformattare il volume". Sì, sul serio. : /
HopelessN00b

25

Potrebbe essere un punto di giunzione ricorsivo. Una cosa del genere può essere creata con junctionun'utilità di file e disco di Sysinternals .

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

E ora puoi andare all'infinito c: \ Hello \ Hello \ Hello .... (bene fino al raggiungimento di MAX_PATH, 260 caratteri per la maggior parte dei comandi ma 32.767 caratteri per alcune funzioni dell'API di Windows).

Un elenco di directory mostra che si tratta di un nodo:

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

Per eliminare utilizzare l'utilità di giunzione:

junction -d c:\Hello\Hello

4
Sfortunatamente, un DIRsolo mi mostra semplici directory ole - nessun segno di giunzioni temo
KenD

2
Puoi fare un doppio controllo rapido con junction -s C:\Storage\Folder1 ?
Brian,

3
No reparse points found:(
KenD,

3
Mi chiedo cosa abbia creato un tale casino di directory secondarie reali.
Brian,

2
Usare dir /aper vedere '<JUNCTION>' senza specificare il nome specifico.
Chloe,

16

Non è una risposta, ma non ho abbastanza rappresentante per un commento.

Una volta ho risolto questo problema su un enorme disco FAT16 da 500 MB su un sistema MS-DOS. Ho usato il debug DOS per scaricare e analizzare manualmente la tabella delle directory. Ho quindi girato un bit per contrassegnare la directory ricorsiva come eliminata. La mia copia di "Riferimenti" dei programmatori DOS di Dettman e Wyatt mi ha mostrato la strada.

Sono ancora straordinariamente orgoglioso di questo. Sarei stupito e terrorizzato se ci fosse uno strumento per scopi generici che ha un tale potere sui volumi FAT32 o NTFS. La vita era più semplice allora.


8
Direi che ne sei giustamente orgoglioso.
mfinni,

3
+1 ho qualche rappresentante su di me. Bella soluzione.
Chris Thornton,

3
Ora puoi eliminare la prima frase della tua risposta.
AL

Questa non è una risposta È una storia su un diverso sistema operativo e un diverso filesystem. Oltre a non essere d'aiuto per questo problema (NT, NTFS), non aiuterebbe nemmeno qualcuno con lo stesso problema (DOS, FAT16) perché in realtà non contiene alcun dettaglio.
Andrew Medico

@Andrew Medico: sono d'accordo con te, quindi la mia prima frase. Ma ti dico dove trovare le informazioni per risolvere il problema leggermente correlato.
Richard,

8

Java può anche gestire percorsi di file lunghi. E può farlo anche molto più velocemente. Questo codice (che ho copiato dalla documentazione dell'API Java) eliminerà una struttura di directory profonda a 1600 livelli in circa 1 secondo (in Windows 7, Java 8.0) e senza rischi di overflow dello stack poiché in realtà non utilizza la ricorsione.

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
Ti odio. Mi hai costretto a votare una risposta che implica l'uso di Java, e ora mi sento tutto sporco. Ho bisogno di una doccia.
HopelessN00b,

Mie scuse. Spero che alla fine ti riprenderai dal tuo trauma. Ma un linguaggio sviluppato da Microsoft (c #) è davvero molto meglio?
SpiderPig,

5
Sono principalmente un ragazzo di Windows in questi giorni, quindi sì, sì lo è. Potrei anche essere amaro e distorto nei confronti di Java a causa della necessità di mantenere 5 versioni specifiche e diverse di JRE su quasi 1.000 client nell'ambiente del mio datore di lavoro, uno dei quali risale al 2009, a causa della schifezza, del malware ... suite di software "enterprise-y" che utilizziamo per applicazioni business-critical.
HopelessN00b,

6

Non hai bisogno di nomi di percorso lunghi se entri chdirnella directory e usi solo i percorsi relativi a rmdir.

Oppure, se è installata una shell POSIX, o portarla sull'equivalente 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

(L'uso di una variabile shell per tracciare dove l'hai rinominata per la condizione del loop è l'altra alternativa allo svolgersi del loop come ho fatto lì.)

Questo evita il sovraccarico della CPU della soluzione di KenD, che forza il sistema operativo ad attraversare l'albero dall'alto verso il nlivello th ogni volta che viene aggiunto un nuovo livello, controllando le autorizzazioni ecc. Quindi ha una sum(1, n) = n * (n-1) / 2 = O(n^2)complessità temporale. Le soluzioni che strappano un pezzo dall'inizio della catena dovrebbero essere O(n), a meno che Windows non debba attraversare un albero quando rinomina la sua directory padre. (Linux / Unix no.) Dovrebbero esserci anche soluzioni che chdirarrivano fino in fondo all'albero e usano percorsi relativi da lì, rimuovendo le directory durante il chdirbackup O(n), supponendo che il sistema operativo non debba controllare tutti i le directory padre ogni chiamata di sistema, quando fai cose mentre sei registrato da qualche parte.

find Folder1 -depth -execdir rmdir {} +eseguirà rmdir mentre viene registrato nella directory più profonda. O in realtà, l' -deleteopzione find funziona su directory e implica -depth. Quindi find Folder1 -deletedovrebbe fare esattamente la stessa cosa, ma più velocemente. Sì, GNU trova su Linux che discende scansionando una directory, passando da CD a sottodirectory con percorsi relativi, quindi rmdircon un percorso relativo, quindi chdir(".."). Non esegue nuovamente la scansione delle directory durante l'ascesa, quindi consumerebbe O(n)RAM.

E 'stato davvero un'approssimazione: stracespettacoli utilizza EFFETTIVAMENTE unlinkat(AT_FDCWD, "tmp", AT_REMOVEDIR), open("..", O_DIRECTORY|...)e fchdir(the fd from opening the directory), con un gruppo di fstatchiamate mescolati in, anche. Ma l'effetto è lo stesso se l'albero delle directory non viene modificato mentre find è in esecuzione.

modifica: Solo per i calci, l'ho provato su GNU / Linux (Ubuntu 14.10, su una CPU Core2Duo di prima generazione da 2,4 GHz, su un filesystem XFS su un'unità WD 2.5 TB 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 crea una directory e tutti i componenti del percorso mancanti).

Sì, davvero 0,05 secondi per operazioni 2k rmdir. xfs è abbastanza bravo a raggruppare insieme le operazioni di metadati nel diario, poiché hanno corretto le operazioni sui metadati essendo lente come 10 anni fa.

Su ext4, la creazione ha richiesto 0m0.279s, l'eliminazione con find ha richiesto ancora 0m0.074s.


si incuriosì e lo provò su Linux. Risulta che gli strumenti GNU standard vanno bene con percorsi lunghi, perché ricorrono all'albero invece di provare a effettuare chiamate di sistema con percorsi lunghi giganti. Anche mkdir va bene quando lo passi su un percorso 38k dalla riga di comando!
Peter Cordes,

0

Mi sono imbattuto nello stesso problema con un pasticcio di cartelle profondo 5000+ che ha fatto qualche applicazione Java e ho scritto un programma che ti aiuterà a rimuovere questa cartella. L'intero codice sorgente è in questo link:

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

Ha rimosso tutto dopo un po ', ma è riuscito a fare il lavoro, spero che aiuti le persone che (come me), incontrano lo stesso problema frustrante


Si prega di non pubblicare risposte solo link. Dovresti inserire le informazioni più importanti dal link nel post stesso e fornire il link come riferimento.
Frederik Nielsen,

Oh scusa, è un programma e non volevo davvero pubblicare TUTTO il codice sorgente qui ... Penso che sia abbastanza chiaro che ho scritto un programma e lo sto ospitando seguendo questo link, e la motivazione e tutto è dentro la risposta, quindi mi sembra abbastanza chiaro che non è una risposta solo link, tuttavia, specificherò (più chiaramente) che è un software progettato per essere eseguito per risolvere questo problema
Imanol Barba Sabariego

0

Anch'io avevo questo, su un sistema Windows 10 autonomo. C: \ Utente \ Nome \ Ripeti \ Ripeti \ Ripeti \ Ripeti \ Ripeti \ Ripeti \ Ripeti apparentemente all'infinito.

Potrei navigare usando Windows o il prompt dei comandi fino al 50esimo e non oltre. Non sono riuscito a eliminarlo o fare clic su di esso, ecc.

C è il mio linguaggio, quindi alla fine ho scritto un programma con un ciclo di chiamate di sistema, che si ripetono fino a quando falliscono. Puoi farlo in qualsiasi lingua, anche in batch DOS. Ho creato una directory chiamata tmp e ho spostato Repeat \ Repeat in quella, ho cancellato la cartella Repeat ora vuota e ho spostato tmp \ Repeat nella cartella corrente. Ancora e ancora!

 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 esegue solo una chiamata di sistema () e controlla il valore restituito, arrestandosi se fallito.

È importante sottolineare che è fallito più volte. Pensavo che forse il mio programma non funzionasse o che dopo tutto fosse infinitamente lungo. Tuttavia, l'ho già avuto in precedenza con le chiamate di sistema, con le cose che non si sincronizzavano, quindi ho appena eseguito di nuovo il programma e ha continuato da dove era stato interrotto, quindi non pensare immediatamente che il tuo programma non funzioni. Quindi, in totale, dopo averlo eseguito circa 20 volte, li ha cancellati tutti. In totale, in origine erano circa 1280 cartelle. Non ho idea di cosa l'abbia causato. Pazzo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.