Каким-то образом в одном из наших старых серверов 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 выйдет из строя.Может ли кто-нибудь предложить способ навсегда убить эту папку?
Всем спасибо за полезный совет.
Попав на территорию 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 папок. Понятия не имею, что вызвало это. Псих.