Назад | Перейти на главную страницу

Удаление (очевидно) бесконечно рекурсивной папки

Каким-то образом в одном из наших старых серверов Server 2008 (не R2) образовалась, казалось бы, бесконечно повторяющаяся папка. Это игра в хаос с нашими резервными копиями, поскольку агент резервного копирования пытается вернуться в папку и никогда не возвращается.

Структура папок выглядит примерно так:

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

... и так далее. Это как один из тех Множества Мандельброта мы все играли в 90-е.

Я пробовал:

Может ли кто-нибудь предложить способ навсегда убить эту папку?

Всем спасибо за полезный совет.

Попав на территорию 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--;
        }
    }
}

Может быть рекурсивная точка соединения. Такую вещь можно создать с junction файловая и дисковая утилита из Sysinternals.

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

И теперь вы можете бесконечно спускаться по c: \ Hello \ Hello \ Hello .... (пока не будет достигнут MAX_PATH, 260 символов для большинства команд, но 32 767 символов для некоторых функций Windows API).

Список каталогов показывает, что это перекресток:

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

Не ответ, но у меня недостаточно комментариев для комментария.

Однажды я исправил эту проблему на тогда огромном диске FAT16 объемом 500 МБ в системе MS-DOS. Я использовал отладку DOS, чтобы вручную выгрузить и проанализировать таблицу каталогов. Затем я перевернул один бит, чтобы пометить рекурсивный каталог как удаленный. Моя копия «Справочника программистов DOS» Деттмана и Вятта указала мне путь.

Я до сих пор чрезвычайно горжусь этим. Я был бы поражен и напуган, если бы существовал какой-либо универсальный инструмент, обладающий такой властью над томами FAT32 или NTFS. Тогда жизнь была проще.

Java также может работать с длинными путями к файлам. И он может делать это намного быстрее. Этот код (который я скопировал из документации Java API) удалит глубинную структуру каталогов 1600 примерно за 1 секунду (в Windows 7, Java 8.0) и без риска переполнения стека, поскольку он фактически не использует рекурсию.

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"));
  }
}

Вам не нужны длинные пути, если вы 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, что заставляет ОС перемещаться по дереву сверху вниз. n-го уровня каждый раз, когда добавляется новый уровень, проверяются разрешения и т.д. sum(1, n) = n * (n-1) / 2 = O(n^2) временная сложность. Решения, которые отрезают кусок от начала цепочки, должны быть O(n), если Windows не нужно перемещаться по дереву при переименовании родительского каталога. (Linux / Unix этого не делает.) Решения, которые chdir вплоть до самого низа дерева и используйте относительные пути оттуда, удаляя каталоги по мере их chdir резервное копирование, также должно быть O(n), предполагая, что ОС не нужно проверять все ваши родительские каталоги при каждом системном вызове, когда вы делаете что-то, пока где-то записаны.

find Folder1 -depth -execdir rmdir {} + запустит rmdir, пока CD находится в самом глубоком каталоге. Или на самом деле найти -delete опция работает с каталогами и подразумевает -depth. Так find Folder1 -delete должен делать то же самое, но быстрее. Да, GNU find в Linux спускается, сканируя каталог, записывая компакт-диск в подкаталоги с относительными путями, а затем rmdir с относительным путем, то chdir(".."). Он не проверяет каталоги повторно при возрастании, поэтому будет потреблять O(n) ОЗУ.

Это действительно было приближение: strace показывает, что он ДЕЙСТВИТЕЛЬНО использует unlinkat(AT_FDCWD, "tmp", AT_REMOVEDIR), open("..", O_DIRECTORY|...), и fchdir(the fd from opening the directory), с кучей fstat звонки тоже смешались. Но эффект будет таким же, если дерево каталогов не изменяется во время работы find.

edit: Просто для удовольствия, я попробовал это на GNU / Linux (Ubuntu 14.10, на процессоре Core2Duo первого поколения 2,4 ГГц, в файловой системе XFS на диске WD Green Power 2,5 ТБ (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 создает каталог и все отсутствующие компоненты пути).

Да реально 0,05 секунды для 2к rmdir ops. xfs неплохо справляется с группировкой операций с метаданными в журнале, поскольку они исправили медленные операции с метаданными, как 10 лет назад.

На ext4 создание заняло 0m0.279s, удаление с find все еще заняло 0m0.074s.

Я столкнулся с той же проблемой с беспорядком папок в 5000+ каталогов, что и в некоторых приложениях Java, и я написал программу, которая поможет вам удалить эту папку. Весь исходный код находится по этой ссылке:

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

Через некоторое время он удалил все это, но ему удалось выполнить свою работу, я надеюсь, что это поможет людям, которые (как и я) сталкиваются с той же неприятной проблемой.

У меня тоже было это, но в автономной системе 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 папок. Понятия не имею, что вызвало это. Псих.