我正在使用自定义函数在8TB驱动器(数千个文件)上实质上执行DIR命令(递归文件列表)。
我的第一次迭代是:
$results = $PATHS | % {Get-FolderItem -Path "$($_)" } | Select Name,DirectoryName,Length,LastWriteTime
$results | Export-CVS -Path $csvfile -Force -Encoding UTF8 -NoTypeInformation -Delimiter "|"
这导致了一个巨大的 $ results 变量,并通过在执行处理时使用powershell进程加速使用99%-100%的CPU来减慢系统速度。
我决定使用管道的功能直接写入CSV文件(可能是释放内存),而不是保存到中间变量,并提出了这个:
$PATHS | % {Get-FolderItem -Path "$($_)" } | Select Name,DirectoryName,Length,LastWriteTime | ConvertTo-CSV -NoTypeInformation -Delimiter "|" | Out-File -FilePath $csvfile -Force -Encoding UTF8
这似乎工作正常(CSV文件正在增长......并且CPU似乎稳定)但是当CSV文件大小达到~200MB时突然停止,并且控制台的错误是“管道已停止“。
我不确定CSV文件大小与错误消息有什么关系,但是我无法使用任何一种方法处理这个大目录!关于如何让这个过程成功完成的任何建议?
答案 0 :(得分:5)
Get-FolderItem运行robocopy
列出文件并将其输出转换为PSObject数组。这是一个缓慢的操作,严格来说,这对于实际任务来说并不是必需的。与foreach 语句相比,流水线操作也增加了很大的开销。在数千或数十万次重复的情况下变得明显。
我们可以将流程加速到任何流水线操作之外,标准的PowerShell cmdlet可以在10秒内为SSD驱动器上的400,000个文件写入信息。
IO.DirectoryInfo
&#39; s EnumerateFileSystemInfos以非阻塞管道方式枚举文件; < / LI>
foreach
声明,它不需要为每个项目创建ScriptBlock上下文,因此它比ForEach
cmdlet IO.StreamWriter
以非阻塞管道方式立即写出每个文件的信息; \\?\
prefix trick解除260个字符的路径长度限制;
function List-PathsInCsv([string[]]$PATHS, [string]$destination) {
$prefix = '\\?\' #' UNC prefix lifts 260 character path length restriction
$writer = [IO.StreamWriter]::new($destination, $false, [Text.Encoding]::UTF8, 1MB)
$writer.WriteLine('Name|Directory|Length|LastWriteTime')
$queue = [Collections.Generic.Queue[string]]($PATHS -replace '^', $prefix)
$numFiles = 0
while ($queue.Count) {
$dirInfo = [IO.DirectoryInfo]$queue.Dequeue()
try {
$dirEnumerator = $dirInfo.EnumerateFileSystemInfos()
} catch {
Write-Warning ("$_".replace($prefix, '') -replace '^.+?: "(.+?)"$', '$1')
continue
}
$dirName = $dirInfo.FullName.replace($prefix, '')
foreach ($entry in $dirEnumerator) {
if ($entry -is [IO.FileInfo]) {
$writer.WriteLine([string]::Join('|', @(
$entry.Name
$dirName
$entry.Length
$entry.LastWriteTime
)))
} else {
$queue.Enqueue($entry.FullName)
}
if (++$numFiles % 1000 -eq 0) {
Write-Progress -activity Digging -status "$numFiles files, $dirName"
}
}
}
$writer.Close()
Write-Progress -activity Digging -Completed
}
用法:
List-PathsInCsv 'c:\windows', 'd:\foo\bar' 'r:\output.csv'
答案 1 :(得分:1)
不要使用robocopy,请使用本机PowerShell命令,如下所示:
$PATHS = 'c:\temp', 'c:\temp2'
$csvfile='c:\temp\listresult.csv'
$PATHS | % {Get-ChildItem $_ -file -recurse } | Select Name,DirectoryName,Length,LastWriteTime | export-csv $csvfile -Delimiter '|' -Encoding UTF8 -NoType
没有纯粹主义者的简短版本:
$PATHS | % {gci $_ -file -rec } | Select Name,DirectoryName,Length,LastWriteTime | epcsv $csvfile -D '|' -E UTF8 -NoT