如何优化因Get-ChildItem耗尽内存的PowerShell共享统计脚本
优化PowerShell共享统计脚本解决内存耗尽问题
原脚本的核心问题是Get-ChildItem -Recurse的结果被完整存入$GetObjectInfo变量,大型共享中的海量文件/文件夹对象会直接耗尽系统内存。以下是针对这个问题的具体优化方案:
核心优化思路
- 流式处理,不缓存全部对象:通过管道直接传递
Get-ChildItem的输出,处理完单个对象就释放内存,避免一次性加载所有内容。 - 单次遍历完成多维度统计:在一次遍历中同时计算文件数、文件夹数和总大小,避免多次重复遍历对象集合,减少内存占用和运行时间。
- 精简不必要的对象调用:合并重复的文件属性读取操作,减少系统调用开销。
优化后的完整脚本
$ScopeName = Read-Host "Enter scope name to gather data on" $SavePath = Read-Host "Path to save results and log to" $SaveCSVPath = "$SavePath\ShareData.csv" $TranscriptLog = "$SavePath\Transcript.log" Write-Host Start-Transcript -Path $TranscriptLog $StartTime = Get-Date $Start = $StartTime.DateTime $Exclusions = { $_.Description -ne "Remote Admin" -and $_.Description -ne "Default Share" -and $_.Description -ne "Remote IPC" } $FileShares = Get-SmbShare -ScopeName $ScopeName | Where-Object $Exclusions $Count = $FileShares.Count Write-Host Write-Host "Gathering data for $Count shares" -ForegroundColor Green Write-Host Write-Host "Results will be saved to $SaveCSVPath" -ForegroundColor Green Write-Host foreach ($FileShare in $FileShares) { $ShareName = $FileShare.Name $Path = $FileShare.Path Write-Host "Working on: $ShareName - $Path" -ForegroundColor Yellow # 初始化统计变量 $totalSize = 0 $fileCount = 0 $folderCount = 0 # 流式遍历文件和文件夹,不缓存全部对象 Get-ChildItem -Path $Path -Recurse -Force -ErrorAction SilentlyContinue | ForEach-Object { if ($_.PSIsContainer) { $folderCount++ } else { $fileCount++ # 累加文件大小,跳过无长度的文件(如快捷方式) if ($null -ne $_.Length) { $totalSize += $_.Length } } } # 计算不同单位的大小 $ObjectSizeMB = "{0:N2}" -f ($totalSize / 1MB) $ObjectSizeGB = "{0:N2}" -f ($totalSize / 1GB) $ObjectSizeTB = "{0:N2}" -f ($totalSize / 1TB) # 一次性读取共享路径的属性,避免重复调用 $shareProps = Get-ItemProperty -Path $Path $ACL = Get-Acl -Path $Path $Table = [PSCustomObject]@{ 'ScopeName' = $FileShare.ScopeName 'Sharename' = $ShareName 'SharePath' = $Path 'Owner' = $ACL.Owner 'Permissions' = $ACL.AccessToString 'LastAccess' = $shareProps.LastAccessTime 'LastWrite' = $shareProps.LastWriteTime 'Size (MB)' = $ObjectSizeMB 'Size (GB)' = $ObjectSizeGB 'Size (TB)' = $ObjectSizeTB 'Total File Count' = $fileCount 'Total Folder Count' = $folderCount 'Total Item Count' = ($fileCount + $folderCount) } $Table | Export-CSV -Path $SaveCSVPath -Append -NoTypeInformation } $EndTime = Get-Date $End = $EndTime.DateTime Write-Host Write-Host "Script start time: $Start" -ForegroundColor Green Write-Host "Script end time: $End" -ForegroundColor Green Write-Host $ElapsedTime = $EndTime - $StartTime Write-Host "Elapsed time: $($ElapsedTime.Days) Days $($ElapsedTime.Hours) Hours $($ElapsedTime.Minutes) Minutes $($ElapsedTime.Seconds) Seconds $($ElapsedTime.Milliseconds) Milliseconds" -ForegroundColor Cyan Write-Host Write-Host "Results saved to $SaveCSVPath" -ForegroundColor Green Write-Host Write-Host "Transcript saved to $TranscriptLog" -ForegroundColor Green Write-Host Stop-Transcript
关键优化点说明
- 移除全量对象缓存:不再将
Get-ChildItem的结果存入变量,而是直接通过ForEach-Object逐个处理,每个对象处理完成后立即被垃圾回收,不会占用大量内存。 - 单次遍历完成统计:在
ForEach-Object中同时统计文件数、文件夹数和总大小,避免了原脚本中多次过滤$GetObjectInfo的重复操作,大幅提升效率。 - 合并属性读取:将原脚本中两次
Get-ItemProperty调用合并为一次,减少系统IO开销。 - 空值判断:对文件长度做了空值判断,避免因特殊文件(如快捷方式、零字节文件)导致的计算错误。
内容的提问来源于stack exchange,提问作者ShaynG
相关产品推荐
相关产品推荐

