Как разбить огромную папку?

У нас есть папка в Windows, которая... огромная. Я запустил "dir > list.txt". Команда потеряла ответ через 1,5 часа. Выходной файл составляет около 200 МБ. Он показывает, что по меньшей мере 2,8 миллиона файлов. Я знаю, что ситуация глупа, но позвольте сосредоточиться на самой проблеме. Если у меня есть такая папка, как я могу разбить ее на некоторые "управляемые" подпапки? Удивительно, но все решения, которые я придумал, включают в себя получение всех файлов в папке в какой-то момент, что в моем случае не имеет значения. Любые предложения?

Спасибо Киту Хилл и Мехрдаду. Я принял ответ Кейта, потому что именно то, что я хотел сделать, но я не мог быстро заставить PS работать быстро.

С советом Мехрдада я написал эту небольшую программу. Потребовалось 7 часов, чтобы переместить 2,8 миллиона файлов. Итак, начальная команда dir закончила. Но почему-то он не вернулся к консоли.

namespace SplitHugeFolder
{
    class Program
    {
        static void Main(string[] args)
        {
            var destination = args[1];

            if (!Directory.Exists(destination))
                Directory.CreateDirectory(destination);

            var di = new DirectoryInfo(args[0]);

            var batchCount = int.Parse(args[2]);
            int currentBatch = 0;

            string targetFolder = GetNewSubfolder(destination);

            foreach (var fileInfo in di.EnumerateFiles())
            {
                if (currentBatch == batchCount)
                {
                    Console.WriteLine("New Batch...");
                    currentBatch = 0;
                    targetFolder = GetNewSubfolder(destination);
                }

                var source = fileInfo.FullName;
                var target = Path.Combine(targetFolder, fileInfo.Name);
                File.Move(source, target);
                currentBatch++;
            }
        }

        private static string GetNewSubfolder(string parent)
        {
            string newFolder;
            do
            {
                newFolder = Path.Combine(parent, Path.GetRandomFileName());
            } while (Directory.Exists(newFolder));
            Directory.CreateDirectory(newFolder);
            return newFolder;
        }
    }
}

Ответ 1

Я использую Get-ChildItem для индексации всего моего C: диска каждую ночь в c:\filelist.txt. Это около 580 000 файлов, а результирующий размер файла составляет ~ 60 МБ. По общему признанию, я нахожусь на Win7 x64 с 8 ГБ оперативной памяти. Тем не менее, вы можете попробовать что-то вроде этого:

md c:\newdir
Get-ChildItem C:\hugedir -r | 
    Foreach -Begin {$i = $j = 0} -Process { 
        if ($i++ % 100000 -eq 0) { 
            $dest = "C:\newdir\dir$j"
            md $dest
            $j++ 
        }
        Move-Item $_ $dest 
    }

Ключ должен сделать движение в потоковом режиме. То есть, не собирайте все результаты Get-ChildItem в одну переменную, а затем продолжайте. Для этого потребуется сразу 2,8 миллиона файлов FileInfos. Кроме того, если вы используете параметр Name в Get-ChildItem, он выведет одну строку, содержащую путь к файлу относительно базового каталога. Даже тогда, возможно, этот размер просто подавит доступную вам память. И, несомненно, для выполнения потребуется немало времени. IIRC правильно, мое индексирование script занимает несколько часов.

Если это сработает, вы должны завершить с c:\newdir\dir0 thru dir28, но опять же, я вообще не тестировал этот script, поэтому ваш пробег может отличаться. Кстати, этот подход предполагает, что вы огромный дир - довольно плоский каталог.

Обновление: Использование параметра Name почти в два раза медленнее, поэтому не используйте этот параметр.

Ответ 2

Я выяснил, что GetChildItem является самым медленным вариантом при работе со многими элементами в каталоге.

Посмотрите на результаты:

Measure-Command { Get-ChildItem C:\Windows -rec | Out-Null }
TotalSeconds      : 77,3730275
Measure-Command { listdir C:\Windows | Out-Null } 
TotalSeconds      : 20,4077132
measure-command { cmd /c dir c:\windows /s /b | out-null }
TotalSeconds      : 13,8357157

(с функцией listdir, определенной следующим образом:

function listdir($dir) {
    $dir
    [system.io.directory]::GetFiles($dir)
    foreach ($d in [system.io.directory]::GetDirectories($dir)) {
        listdir $d
    }
}

)

С учетом этого, что бы я сделал: я бы остался в PowerShell, но использовал более низкий уровень подхода с методами .NET:

function DoForFirst($directory, $max, $action) {
    function go($dir, $options)
    {
        foreach ($f in [system.io.Directory]::EnumerateFiles($dir))
        {
            if ($options.Remaining -le 0) { return }
            & $action $f
            $options.Remaining--
        }
        foreach ($d in [system.io.directory]::EnumerateDirectories($dir))
        {
            if ($options.Remaining -le 0) { return }
            go $d $options
        }
    }
    go $directory (New-Object PsObject -Property @{Remaining=$max })
}
doForFirst c:\windows 100 {write-host File: $args }
# I use PsObject to avoid global variables and ref parameters.

Чтобы использовать код, вы должны переключиться на .NET 4.0 runtime - перечисляющие методы новы в .NET 4.0.

Вы можете указать любой скриптовый блок как параметр -action, поэтому в вашем случае это будет что-то вроде {Move-item -literalPath $args -dest c:\dir }.

Просто попробуйте перечислить первые 1000 предметов, я надеюсь, что он закончится очень быстро:

doForFirst c:\yourdirectory 1000 {write-host '.' -nonew }

И, конечно, вы можете обрабатывать все элементы одновременно, просто используйте

doForFirst c:\yourdirectory ([long]::MaxValue) {move-item ... }

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

Ответ 3

Как насчет того, чтобы начать с этого:   cmd/c dir/b > list.txt

Это даст вам список всех имен файлов.

Если вы делаете "dir > list.txt" из приглашения powershell, get-childitem псевдонимом называется "dir". Get-childitem знает проблемы, перечисляющие большие каталоги, и возвращаемые им коллекции объектов могут стать огромными.