Я обнаружил, что 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 - методы перечисления являются новыми в .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 ... }
, и каждый элемент должен быть обработан сразу после его возврата.Таким образом, весь список не читается сразу, а затем обрабатывается, но обрабатывается во время чтения.