Видалення (мабуть) нескінченно рекурсивної папки


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.

Хтось може запропонувати спосіб знищити цю папку назавжди?


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

2
Я спробував, навіть використовуючи версію Delimon .Delete(замість звичайної System.IOверсії), і хоча він не кидав винятку, він, схоже, нічого не робив. Безумовно, рекурсія за допомогою вищевказаного методу зайняла віки, і .Deleteлише жувала речі протягом 5-10 секунд. Можливо, він відколовся в декількох каталогах, а потім здався?
КенД

4
Ви коли-небудь розуміли, як це сталося для початку? Здається, що помилка деяких дійсно написаних програм користувача.
Парфянський розстріл

8
Повторюється у функції 1600 разів? Стек справді переповнення території!
Олександр Дубінський

2
Як убік, чи папки були заповнені чим-небудь? Якщо ви зможете визначити, через які проміжки часу створювалися рекурсивні папки, і помножити їх на кількість рекурсій, ви (сподіваємось) отримаєте приблизний часовий
проміжок

8
Нічого собі, радий, що ти вирішив це. FYI, офіційний Microsoft підтримує виправлення подібної ситуації "переформатувати обсяг". Так, серйозно. : /
HopelessN00b

25

Може бути рекурсивна точка з'єднання. Таку річ можна створити за junctionдопомогою утиліти файлів і дисків від Sysinternals .

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

Тепер ви можете нескінченно спускатися вниз c: \ Hello \ Hello \ Hello .... (добре, поки не буде досягнуто MAX_PATH, 260 символів для більшості команд, але 32 767 символів для деяких функцій API 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

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 Programmers' Reference 'показала мені шлях.

Я досі нестримно пишаюся цим. Я був би вражений і жахнувся, якщо є якийсь інструмент загального призначення, який має таку потужність над томами FAT32 або NTFS. Тоді життя було простішим.


8
Я б сказав, що ви з цим виправдано пишаєтесь.
mfinni

3
+1 мають на мене кілька представників. Приємне рішення.
Кріс Торнтон

3
Тепер ви можете видалити перше речення своєї відповіді.
AL

Це не відповідь. Це історія про іншу операційну систему та іншу файлову систему. Окрім того, що не допоможе цій проблемі (NT, NTFS), вона навіть не допоможе комусь із тією ж проблемою (DOS, FAT16), оскільки вона насправді не містить деталей.
Ендрю Медіко

@Andrew Medico: Я згоден з тобою, звідси і моє перше речення. Але я вам кажу, де знайти інформацію для вирішення трохи пов'язаної проблеми.
Річард

8

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

3
Я тебе ненавиджу. Ви змусили мене подати відповідь, що стосується використання Java, і тепер я відчуваю себе всім брудним. Мені потрібен душ.
HopelessN00b

Мої вибачення. Сподіваюся, ви зрештою одужаєте від своєї травми. Але чи справді мова, розроблена Microsoft (c #), набагато краща?
SpiderPig

5
Я в першу чергу хлопець Windows сьогодні, так що так, так. Я також можу бути гірким і упередженим щодо Java через те, що потрібно підтримувати 5 специфічних, різних версій JRE для майже 1000 клієнтів у середовищі мого роботодавця, одна з яких датується 2009 роком, зважаючи на лайно ... е, шкідливе програмне забезпечення ... е-е, програмні комплекти "підприємство-у", які ми використовуємо для критично важливих бізнес-додатків.
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рівня щоразу, коли додається новий рівень, перевіряючи дозволи та ін. Отже, це має 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 під час компакт-диска у найглибший каталог. Або насправді, -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, на 2,4 ГГц Core2Duo процесора першого покоління, у файловій системі 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 секунди за 2 кп rmdir. xfs досить добре поєднує операції з метаданими разом у журналі, оскільки вони фіксували, що метадані можуть бути повільними, як 10 років тому.

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


зацікавився і спробував це в Linux. Виявляється, у стандартних інструментів GNU все добре з довгими шляхами, оскільки вони повторюються вниз по дереву, а не намагаються робити системні дзвінки з гігантськими довгими шляхами. Навіть mkdir добре, коли ви передаєте йому шлях 38k в командному рядку!
Пітер Кордес

0

Я зіткнувся з тією самою проблемою з безладною папкою, що містить більш ніж 5000 директорій, і деякі програми Java, і я написав програму, яка допоможе вам видалити цю папку. Весь вихідний код знаходиться за цим посиланням:

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

Через деякий час це все усунуло, але вдалося виконати роботу, я сподіваюся, що це допомагає людям, які (як і я) стикаються з тим самим розчаруючим питанням


Будь ласка, не публікуйте відповідей лише для посилань. Ви повинні розмістити найважливішу інформацію із посилання в самому дописі та надати посилання для довідки.
Фредерік Нільсен

Пробачте, це програма, і я дійсно не хотів розміщувати тут ВСІ вихідні коди ... Я думаю, це цілком зрозуміло, що я написав програму, і я розміщую її за цим посиланням, і мотивація, і все в відповідь, тому мені здається досить зрозумілим, що це не відповідь, що стосується лише посилання, тим не менш я уточню (більш чітко), що це програмне забезпечення, призначене для вирішення цієї проблеми
Imanol Barba Sabariego

0

У мене теж це було, хоча в автономній системі Windows 10, хоча. C: \ Користувач \ Ім'я \ Повторити \ Повторити \ Повторити \ Повторити \ Повторити \ Повторити \ Повторити, здавалося б, до нескінченності.

Я міг перейти за допомогою Windows або командного рядка до 50-го, і далі. Я не зміг її видалити або натиснути на неї тощо.

C - це моя мова, тому врешті-решт я написав програму з циклом системних викликів, які повторюються, поки вони не завершаться. Ви можете зробити це будь-якою мовою, навіть DOS-пакет. Я створив каталог під назвою tmp і перемістив Повторити \ Повторити в цьому, видалив порожню папку Повторення і перемістив tmp \ Повторити назад у поточну папку. Знову і знову!

 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 просто запускає системний () виклик і перевіряє повернене значення, зупиняючись, якщо воно не вдалося.

Що важливо, це було кілька разів. Я думав, що, можливо, моя програма не працює, або що вона була нескінченно довгою. Однак у мене це було раніше з системними дзвінками, коли речі не синхронізувалися, тому я просто запустив програму ще раз, і вона продовжувалася з того місця, де вона припинилася, тому не думайте, що ваша програма не працює. Таким чином, після запуску його приблизно 20 разів, він очистив їх усіх. Загалом спочатку це було близько 1280 папок глибоко. Поняття не маю, що це спричинило. Божевільний.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.