我已经完成了一个小项目,该项目将从文件服务器中提取一些信息。为了执行该项目,我创建了一个脚本,该脚本在.csv文件中输出所有信息。问题在于Powershell在此过程中吞噬了我所有计算机的RAM,因为要解析的数据可能高达数百Gb。
这是我的剧本。
$folder = Get-ChildItem -Recurse 'Complete_Path' | select FullName, @{Name="Owner";Expression={(Get-Acl $_.FullName).Owner}}, CreationTime, LastWriteTime, LastAccessTime, PSIsContainer | sort FullName
$output = @()
$folder | foreach {
$type =
if ($_.PSIsContainer -eq "True") {
Write-Output "Folder"
}
else {
Write-Output "File"
}
$size =
if ($_.PSIsContainer -eq "True") {
Get-ChildItem -Recurse $_.FullName | measure -Property Length -Sum -ErrorAction SilentlyContinue | select -ExpandProperty Sum
}
else {
Get-Item $_.FullName | measure -Property Length -Sum -ErrorAction SilentlyContinue | select -ExpandProperty Sum
}
$hash = @{
FullName = $_.FullName
Owner = $_.Owner
CreationTime = $_.CreationTime
LastWriteTime = $_.LastWriteTime
LastAccessTime = $_.LastAccessTime
Type = $type
'Size in MB' = [math]::Round($($size/1Mb),2)
}
$output += New-Object PSObject -Property $hash
}
$output | select FullName, Owner, CreationTime, LastWriteTime, LastAccessTime, Type, 'Size in MB' | Export-Csv C:\myDOCS.csv -Delimiter ";" -NoTypeInformation -Encoding UTF8
你们是否知道我如何更快地完成工作并减少耗油量?提取可能需要几天的时间。
谢谢。
答案 0 :(得分:0)
使用.Net PSObject列表$output=@()
替换Powershell阵列$output = [System.Collections.Generic.List[psobject]]::new()
,并使用该对象的 .Add 方法添加您的物品。
对于较小的列表,您不会注意到,但是使用Powershell数组和 + = 运算符是很大的性能下降。每次您执行+ =时,都会再创建一个数组,以完全重新创建数组。
在您的初始Get-ChildItem语句中包含长度。稍后,您可以测量总和,而不必一直都通过Get-ChildItem
管道在内存上的播放效果不错,但总体运行速度较慢。当性能成为问题时,我倾向于不使用管道。
这样的事情应该已经快得多了
$folder = Get-ChildItem -Recurse "$($env:USERPROFILE)\Downloads" | select FullName, @{Name = "Owner"; Expression = { (Get-Acl $_.FullName).Owner } }, CreationTime, LastWriteTime, LastAccessTime, PSIsContainer, Length | sort FullName
$output = [System.Collections.Generic.List[psobject]]::new()
foreach ($Item in $folder) {
if ($Item.PSIsContainer) {
$Type = 'Folder'
$size = $folder.Where( { $_.FullName -like $item.FullName }).FullName | measure -Property Length -Sum -ErrorAction SilentlyContinue | select -ExpandProperty Sum
}
else {
$Type = 'File'
$size = $Item.Length
}
$size = [math]::Round($($size / 1Mb), 2)
$hash = @{
FullName = $Item.FullName
Owner = $Item.Owner
CreationTime = $Item.CreationTime
LastWriteTime = $Item.LastWriteTime
LastAccessTime = $Item.LastAccessTime
Type = $Type
'Size in MB' = $size
}
[void]($output.Add((New-Object PSObject -Property $hash)))
}
$output | select FullName, Owner, CreationTime, LastWriteTime, LastAccessTime, Type, 'Size in MB' | Export-Csv C:\myDOCS.csv -Delimiter ";" -NoTypeInformation -Encoding UTF8
您仍然可以改进大小计算,因此首先计算最深的文件夹大小,然后父文件夹可以获取该值并汇总子文件夹,而无需重新计算文件
另一种想法是不立即执行Get-ACl(我怀疑这执行起来很慢)并获取您的项目,执行其余操作,然后并行化Get-ACL,以便您可以在多个并行线程,并将其添加到列表中。
考虑对小批代码进行测试,然后使用Measure-Command来确定代码中最慢的操作在哪里。
我建议您看看有关该主题的一些更高级的主题。 这是一篇很好的入门指南:Slow Code: Top 5 ways to make your Powershell scripts run faster
答案 1 :(得分:0)
在一个管道中处理整个事情是否更好?
Get-ChildItem -Recurse |
select FullName, @{Name="Owner";Expression={(Get-Acl $_.FullName).Owner}},
CreationTime, LastWriteTime, LastAccessTime, PSIsContainer | sort FullName |
foreach {
$type =
if ($_.PSIsContainer -eq "True") {
Write-Output "Folder"
}
else {
Write-Output "File"
}
$size =
if ($_.PSIsContainer -eq "True") {
Get-ChildItem -Recurse $_.FullName |
measure -Property Length -Sum -ErrorAction SilentlyContinue |
select -ExpandProperty Sum
}
else {
Get-Item $_.FullName |
measure -Property Length -Sum -ErrorAction SilentlyContinue |
select -ExpandProperty Sum
}
$hash = @{
FullName = $_.FullName
Owner = $_.Owner
CreationTime = $_.CreationTime
LastWriteTime = $_.LastWriteTime
LastAccessTime = $_.LastAccessTime
Type = $type
'Size in MB' = [math]::Round($($size/1Mb),2)
}
New-Object PSObject -Property $hash
} | select FullName, Owner, CreationTime, LastWriteTime, LastAccessTime,
Type, 'Size in MB' |
Export-Csv myDOCS.csv -Delimiter ";" -NoTypeInformation -Encoding UTF8