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

46

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

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

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

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

Я пробовал:

  • Удаление его из Проводника. Да, я оптимист.
  • RMDIR C:\Storage\Folder1 /Q/S - это возвращается The directory is not empty
  • ROBOCOPY C:\temp\EmptyDirectory C:\Storage\Folder1 /PURGE - это прокручивает папки в течение нескольких минут, прежде чем robocopy.exe вылетает.

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

Kend
источник
1
Я бы попробовал /MIRвместо этого: ROBOCOPY /MIR C:\temp\EmptyDirectory C:\Storage\Folder1также может стоить запустить chkdskтолько для хихиканья.
Jscott
1
/MIRказалось, длился дольше, но в конце концов тоже взорвался («Робокопия перестала работать»). Я немного напуган, чтобы сделать chkdsk; это довольно старый сервер, и я беспокоюсь, что эта проблема свидетельствует о более крупных проблемах файловой системы ...
KenD
7
Попробуйте загрузиться с пробного компакт-диска Linux (Ubuntu / Centos / Fedora / ...) для рабочего стола и удалите папку оттуда.
Гунтрам Блом поддерживает Монику
2
@KenD Если вы подозреваете проблемы с повреждением файловой системы, вам, безусловно, следует сначала попытаться восстановить файловую систему. Попытки удаления каталогов могут ухудшить ситуацию.
Jscott
1
Так как (из вашего ответа ниже), каталог не был бесконечным, только очень глубока, если у вас CygWin или UnxUtils установлен, вы можете использовать , findчтобы сделать удаление глубины первого каталога:find Storage/Folder1 -depth -exec rmdir {} \;
Джонни

Ответы:

45

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

Полностью перейдя на территорию 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--;
        }
    }
}
Kend
источник
2
Я пытался, даже используя версию Delimon .Delete(вместо обычной System.IOверсии), и, хотя она не выдает исключение, она, похоже, ничего не делает. Конечно, рекурсия с использованием описанного выше метода заняла целую вечность, и она .Deleteжевала только 5-10 секунд. Может быть, он откололся в нескольких каталогах, а затем сдался?
Кенд
4
Вы когда-нибудь выясняли, как это случилось с самого начала? Похоже, что некоторые действительно плохо написанные пользовательские программы виноваты.
Парфянский выстрел
8
Повторяется в функцию 1600 раз? Территория переполнения стека действительно!
Александр Дубинский
2
Кроме того, были ли какие-нибудь папки заполнены? Если вы сможете определить, с какими интервалами были созданы рекурсивные папки, и умножить их на количество рекурсий, вы (надеюсь) получите приблизительный период времени, когда эта проблема началась ...
Get-HomeByFiveOClock
8
Вау, рад, что ты решил это. К вашему сведению, официальное исправление, поддерживаемое Microsoft, для такого рода ситуаций заключается в «переформатировании тома». Да, серьезно. : /
HopelessN00b
25

Может быть рекурсивной точкой соединения. Такую вещь можно создать с 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 -d c:\Hello\Hello
Брайан
источник
4
К сожалению, DIRпросто показывает мне простые ole каталоги - я не боюсь никаких переходов
KenD
2
Можете ли вы сделать быструю двойную проверку с junction -s C:\Storage\Folder1 ?
Брайан
3
No reparse points found:(
Кенд
3
Интересно, что создало такой беспорядок в реальных подкаталогах.
Брайан
2
Используйте, dir /aчтобы увидеть «<JUNCTION>» без указания конкретного имени.
Хлоя
16

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

Однажды я исправил эту проблему на огромном 500-мегабайтном диске FAT16 в системе MS-DOS. Я использовал отладку DOS для ручного дампа и анализа таблицы каталогов. Затем я щелкнул один бит, чтобы пометить рекурсивный каталог как удаленный. Моя копия Dettman и Wyatt 'Справочник программистов DOS' показала мне путь.

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

Ричард
источник
8
Я бы сказал, что вы по праву гордитесь этим.
mfinni
3
+1 повесь мне. Хорошее решение.
Крис Торнтон
3
Теперь вы можете удалить первое предложение вашего ответа.
AL
Это не ответ. Это история о другой операционной системе и другой файловой системе. В дополнение к тому, что она не помогает в этой (NT, NTFS) проблеме, она даже не поможет кому-то с такой же проблемой (DOS, FAT16), потому что она на самом деле не содержит никаких деталей.
Эндрю Медико
@ Андрей Медико: Я согласен с вами, отсюда и мое первое предложение. Но я говорю вам, где найти информацию, чтобы решить слегка связанную проблему.
Ричард
8

Java также может работать с длинными путями к файлам. И это может сделать это намного быстрее тоже. Этот код (который я скопировал из документации по API Java) удалит структуру каталогов глубиной 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"));
  }
}
SpiderPig
источник
3
Я ненавижу тебя. Вы заставили меня высказать ответ, который касается использования Java, и теперь я чувствую себя грязным. Мне нужно принять душ.
HopelessN00b
Мои извенения. Я надеюсь, что в конце концов вы оправитесь от травмы. Но действительно ли язык, разработанный Microsoft (c #), намного лучше?
SpiderPig
5
В настоящее время я в основном парень из Windows, так что да, да, это так. Я также могу быть горьким и предвзятым по отношению к Java из-за необходимости поддерживать 5 конкретных, разных версий JRE для почти 1000 клиентов в среде моего работодателя, одна из которых была выпущена в 2009 году из-за вредоносного вредоносного ПО. ... э-э, программные пакеты "enterprise-y", которые мы используем для критически важных бизнес-приложений.
HopelessN00b
6

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

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

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

редактировать: просто для удовольствия, я попробовал это на GNU / Linux (Ubuntu 14.10, на первом процессоре Core2Duo 2,4 ГГц, на файловой системе XFS на диске Green Power WD 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 секунды для 2k rmdir ops. xfs неплохо умеет объединять операции метаданных в журнале, поскольку они исправляют медленные операции метаданных, как 10 лет назад.

На ext4 создание заняло 0m0.279s, удаление с поиском по-прежнему занимало 0m0.074s.

Питер Кордес
источник
стал любопытным и попробовал это на Linux. Оказывается, все стандартные инструменты GNU подходят для длинных путей, потому что они рекурсивно спускаются по дереву вместо того, чтобы пытаться делать системные вызовы с гигантскими длинными путями. Даже mkdir хорош, если вы передаете ему путь 38k в командной строке!
Питер Кордес
0

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

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

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

Иманол Барба Сабариего
источник
Пожалуйста, не публикуйте только ссылки. Вы должны поместить самую важную информацию из ссылки в самом посте и предоставить ссылку для справки.
Фредерик Нильсен
Извините, это программа, и я действительно не хотел публиковать ВСЕ исходный код здесь ... Я думаю, совершенно ясно, что я написал программу, и я размещаю ее по этой ссылке, и мотивация и все в ответ, так что мне кажется, что это не просто ответ, но я уточню (более четко), что это программное обеспечение, предназначенное для решения этой проблемы
Imanol Barba Sabariego
0

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

DenM
источник