Eliminar una carpeta (aparentemente) infinitamente recursiva

46

De alguna manera, uno de nuestros viejos cuadros de Server 2008 (no R2) ha desarrollado una carpeta aparentemente infinitamente recurrente. Esto está haciendo estragos con nuestras copias de seguridad, ya que el agente de copia de seguridad intenta volver a la carpeta y nunca regresa.

La estructura de carpetas se parece a:

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

... y así. Es como uno de esos sets de Mandelbrot con los que solíamos jugar en los años 90.

He intentado:

  • Eliminarlo del Explorador. Sí, soy optimista.
  • RMDIR C:\Storage\Folder1 /Q/S - esto vuelve The directory is not empty
  • ROBOCOPY C:\temp\EmptyDirectory C:\Storage\Folder1 /PURGE - Esto gira a través de las carpetas durante un par de minutos antes de que robocopy.exe se bloquee.

¿Alguien puede sugerir una forma de eliminar esta carpeta para siempre?

KenD
fuente
1
Lo intentaría en su /MIRlugar: ROBOCOPY /MIR C:\temp\EmptyDirectory C:\Storage\Folder1también puede valer la pena correr chkdsksolo por risas
jscott
1
/MIRpareció durar más, pero finalmente fue bombardeado también ("la robocopia ha dejado de funcionar"). Tengo un poco de miedo de hacer un chkdsk; esto es un servidor bastante viejo y me preocupa que este problema es indicativo de grandes problemas en el sistema de archivos ...
KEND
77
Intente arrancar desde un CD de prueba de escritorio Linux (Ubuntu / Centos / Fedora / ...) y elimine la carpeta desde allí.
Guntram Blohm apoya a Monica
2
@KenD Si sospecha que hay problemas de corrupción del sistema de archivos, primero debe intentar reparar el sistema de archivos primero. Intentar trucos de eliminación de directorios podría empeorar las cosas.
jscott
1
Dado que (de su respuesta a continuación), el directorio no era infinito, solo muy profundo, si tenía CygWin o UnxUtils instalados, podría usar findpara hacer una eliminación profunda del primer directorio:find Storage/Folder1 -depth -exec rmdir {} \;
Johnny

Respuestas:

45

Gracias a todos por los útiles consejos.

Desviándome bien en el territorio de StackOverflow, resolví el problema al desbloquear este fragmento de código C #. Utiliza la biblioteca Delimon.Win32.IO que trata específicamente los problemas de acceso a rutas de archivos largas.

En caso de que esto pueda ayudar a alguien más, aquí está el código: superó los ~ 1600 niveles de recursión con los que me había quedado atrapado y me llevó alrededor de 20 minutos eliminarlos a todos.

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
fuente
2
Lo intenté, incluso usando la versión de Delimon .Delete(en lugar de la System.IOversión normal ), y aunque no arrojó una excepción, no pareció hacer nada. Ciertamente, la recursión usando el método anterior tomó años, y .Deletesolo masticó cosas durante 5-10 segundos. ¿Tal vez eliminó algunos directorios y luego se rindió?
KenD
44
¿Alguna vez descubriste cómo sucedió eso al principio? Suena como la falla de algún programa de usuario realmente mal escrito.
Disparo de Partia
8
Recurriendo a una función 1600 veces? ¡Apila el territorio de desbordamiento de hecho!
Aleksandr Dubinsky
2
Como comentario aparte, ¿estaban las carpetas pobladas por algo? Si puede determinar a qué intervalos se crearon las carpetas recursivas y multiplicar eso por el número de recursiones, (con suerte) obtendrá un período aproximado de cuándo comenzó este problema ...
Get-HomeByFiveOClock
8
Wow, me alegra que hayas terminado de resolver esto. Para su información, la solución oficial de Microsoft para este tipo de situación es "reformatear el volumen". Si, en serio : /
HopelessN00b
25

Podría ser un punto de unión recursivo. Tal cosa se puede crear con junctionuna utilidad de archivo y disco de Sysinternals .

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

Y ahora puede ir sin parar hacia abajo c: \ Hello \ Hello \ Hello .... (bueno, hasta llegar a MAX_PATH, 260 caracteres para la mayoría de los comandos pero 32,767 caracteres para algunas funciones de la API de Windows).

Una lista de directorio muestra que es una unión:

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

Para eliminar, use la utilidad de unión:

junction -d c:\Hello\Hello
Brian
fuente
44
Desafortunadamente, un DIRsolo me muestra directorios simples - no hay signos de ningún cruce, me temo
KenD
2
¿Puedes hacer una doble verificación rápida con junction -s C:\Storage\Folder1 ?
Brian
3
No reparse points found:(
KenD
3
Me pregunto qué creó un lío de subdirectorios reales.
Brian
2
Use dir /apara ver '<JUNCTION>' sin especificar el nombre específico.
Chloe
16

No es una respuesta, pero no tengo suficiente representante para un comentario.

Una vez arreglé este problema en un enorme disco FAT16 de 500 MB en un sistema MS-DOS. Usé la depuración de DOS para volcar y analizar manualmente la tabla de directorios. Luego volteé un bit para marcar el directorio recursivo como eliminado. Mi copia de 'Referencia de programadores de DOS' de Dettman y Wyatt me mostró el camino.

Todavía estoy excesivamente orgulloso de esto. Me sorprendería y aterrorizaría si hubiera alguna herramienta de propósito general que tenga tanto poder sobre los volúmenes FAT32 o NTFS. La vida era más simple en aquel entonces.

Ricardo
fuente
8
Yo diría que estás justificadamente orgulloso de esto.
mfinni
3
+1 tiene algún representante sobre mí. Buena solución
Chris Thornton
3
Ahora puede eliminar la primera oración de su respuesta.
AL
Esta no es una respuesta. Es una historia sobre un sistema operativo diferente y un sistema de archivos diferente. Además de no ser de ayuda para este problema (NT, NTFS), ni siquiera ayudaría a alguien con el mismo problema (DOS, FAT16) porque en realidad no contiene ningún detalle.
Andrew Medico
@ Andrew Medico: Estoy de acuerdo contigo, de ahí mi primera oración. Pero sí te digo dónde encontrar la información para resolver el problema ligeramente relacionado.
Richard
8

Java también puede manejar rutas de archivo largas. Y también puede hacerlo mucho más rápido. Este código (que copié de la documentación de la API de Java) eliminará una estructura de directorio profunda de nivel 1600 en aproximadamente 1 segundo (bajo Windows 7, Java 8.0) y sin riesgo de desbordamiento de la pila, ya que en realidad no usa la recursividad.

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"));
  }
}
Puerco araña
fuente
3
Te odio. Me has obligado a votar una respuesta que implica el uso de Java, y ahora me siento muy sucio. Necesito darme una ducha.
HopelessN00b
Mis disculpas. Espero que eventualmente te recuperes de tu trauma. ¿Pero es un lenguaje desarrollado por Microsoft (c #) realmente mucho mejor?
SpiderPig
55
Soy principalmente un chico de Windows en estos días, así que sí, sí lo es. También puedo estar amargado y predispuesto contra Java por tener que mantener 5 versiones diferentes y específicas de JRE en casi 1,000 clientes en el entorno de mi empleador, uno de los cuales se remonta a 2009, debido a la basura ... er, malware ... uh, suites de software "empresarial" que utilizamos para aplicaciones críticas de negocios.
HopelessN00b
6

No necesita nombres de ruta largos si ingresa chdiral directorio y solo usa rutas relativas a rmdir.

O, si tiene un shell POSIX instalado, o lo transfiere al equivalente de 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

(El uso de una variable de shell para rastrear dónde lo renombró para la condición del bucle es la otra alternativa a desenrollar el bucle como lo hice allí).

Esto evita la sobrecarga de la CPU de la solución de KenD, lo que obliga al sistema operativo a atravesar el árbol desde el nivel superior hasta el nnivel th cada vez que se agrega un nuevo nivel, verificando permisos, etc. Por lo tanto, tiene sum(1, n) = n * (n-1) / 2 = O(n^2)complejidad de tiempo. Las soluciones que cortan un fragmento desde el inicio de la cadena deberían ser O(n), a menos que Windows necesite atravesar un árbol al cambiar el nombre de su directorio principal. (Linux / Unix no lo hace). Las soluciones que chdirvan chdirhasta la parte inferior del árbol y usan rutas relativas desde allí, eliminando directorios a medida que se realizan copias de seguridad, también deberían serlo O(n), suponiendo que el sistema operativo no necesita verificar todos sus directorios principales cada llamada al sistema, cuando haces cosas mientras estás en CD en alguna parte.

find Folder1 -depth -execdir rmdir {} +ejecutará rmdir mientras está en CD en el directorio más profundo. O, en realidad, la -deleteopción find funciona en directorios e implica -depth. Entonces find Folder1 -deletedebería hacer exactamente lo mismo, pero más rápido. Sí, GNU encuentra en Linux desciende escaneando un directorio, CD a subdirectorios con rutas relativas, luego rmdircon una ruta relativa, luego chdir(".."). No vuelve a escanear directorios mientras asciende, por lo que consumiría O(n)RAM.

Eso fue realmente una aproximación: straceespectáculos que realmente utiliza unlinkat(AT_FDCWD, "tmp", AT_REMOVEDIR), open("..", O_DIRECTORY|...)y fchdir(the fd from opening the directory), con un montón de fstatllamadas mezclados, también. Pero el efecto es el mismo si el árbol de directorios no se modifica mientras se está ejecutando find.

editar: Solo por diversión, probé esto en GNU / Linux (Ubuntu 14.10, en una CPU Core2Duo de primera generación de 2.4GHz, en un sistema de archivos XFS en una unidad 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 crea un directorio y cualquier componente de ruta faltante).

Sí, realmente 0.05 segundos para 2k rmdir ops. xfs es bastante bueno para agrupar operaciones de metadatos en el diario, ya que repararon que las operaciones de metadatos eran lentas como hace 10 años.

En ext4, crear tomó 0m0.279s, eliminar con find todavía tomó 0m0.074s.

Peter Cordes
fuente
Sentí curiosidad y lo probé en Linux. Resulta que las herramientas GNU estándar están bien con rutas largas, porque se repiten en el árbol en lugar de intentar hacer llamadas al sistema con rutas largas gigantes. ¡Incluso mkdir está bien cuando le pasa una ruta de 38k en la línea de comando!
Peter Cordes
0

Me encontré con el mismo problema con un desorden de carpetas de más de 5000 directorios que algunas aplicaciones Java y escribí un programa que te ayudará a eliminar esta carpeta. El código fuente completo está en este enlace:

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

Eliminó todo después de un tiempo, pero logró hacer el trabajo, espero que ayude a las personas que (como yo) se encuentran con el mismo problema frustrante

Imanol Barba Sabariego
fuente
No publique respuestas de solo enlace. Debe poner la información más importante del enlace en la publicación misma y proporcionar el enlace como referencia.
Frederik Nielsen
Oh, lo siento, es un programa, y ​​realmente no quería publicar TODO el código fuente aquí ... Creo que está bastante claro que escribí un programa y lo estoy alojando siguiendo este enlace, y la motivación y todo está en la respuesta, por lo que me parece bastante claro, no es una respuesta de solo enlace, no obstante, especificaré (más claramente) que es un software destinado a ejecutarse para resolver este problema
Imanol Barba Sabariego
0

Yo también tuve esto, sin embargo, en un sistema Windows 10 independiente. C: \ Usuario \ Nombre \ Repetir \ Repetir \ Repetir \ Repetir \ Repetir \ Repetir \ Repetir aparentemente hasta el infinito.

Podría navegar usando Windows o el símbolo del sistema hasta aproximadamente el número 50 y no más. No pude eliminarlo, ni hacer clic en él, etc.

C es mi lenguaje, así que eventualmente escribí un programa con un ciclo de llamadas al sistema, que se repiten hasta que fallan. Sin embargo, puede hacer esto en cualquier idioma, incluso en lotes de DOS. Hice un directorio llamado tmp y moví Repeat \ Repeat a eso, eliminé la carpeta Repeat ahora vacía, y moví tmp \ Repeat nuevamente a la carpeta actual. ¡Una y otra vez!

 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 simplemente ejecuta una llamada system () y verifica el valor de retorno, deteniéndose si falla.

Es importante destacar que falló varias veces. Pensé que quizás mi programa no estaba funcionando, o que era infinitamente largo después de todo. Sin embargo, he tenido esto antes con llamadas al sistema, con cosas que no se sincronizan, así que simplemente ejecuté el programa nuevamente y continuó desde donde se quedó, así que no piense inmediatamente que su programa no está funcionando. Entonces, en total, después de ejecutarlo unas 20 veces, los eliminó a todos. En total, tenía unas 1280 carpetas de profundidad originalmente. No tengo idea de qué lo causó. Loca.

DenM
fuente