该脚本的目的如下:
到目前为止(3)是艰难的部分。
这是我到目前为止编写和测试的内容。这适用于具有一百甚至一千个文件的文件夹:
$hostname=hostname
$directory = "foo"
$dteCurrentDate = Get-Date –f "yyyy/MM/dd"
$FolderItems = Get-ChildItem $directory -recurse
$Measurement = $FolderItems | Measure-Object -property length -sum
$colitems = $FolderItems | measure-Object -property length -sum
"$hostname;{0:N2}" -f ($colitems.sum / 1MB) + "MB;" + $Measurement.count + " files;" + "$dteCurrentDate"
在包含数百万个文件的文件夹中,$colitems
变量从数百万个文件的信息集中变得如此庞大,导致系统不稳定。有没有更有效的方法来绘制和存储这些信息?
答案 0 :(得分:32)
如果使用流式传输和流水线操作,你应该减少(3)很多问题,因为当你流式传输时,每个对象都会在它们可用时传递给管道并且不占用太多内存而你应该能够处理数百万个文件(虽然需要时间)。
Get-ChildItem $directory -recurse | Measure-Object -property length -sum
我不相信@ Stej的陈述Get-ChildItem probably reads all entries in the directory and then begins pushing them to the pipeline.
是真的。流水线操作是PowerShell的基本概念(提供cmdlet,脚本等支持它)。它既可以确保处理对象在可用时逐个传递,也可以在需要时 。 Get-ChildItem
的行为不会有所不同。
Understanding the Windows PowerShell Pipeline 中提供了一个很好的例子。
引用它:
Out-Host -Paging命令是一个有用的管道元素 你想要慢慢显示冗长的输出。它是 如果操作非常耗费CPU,则特别有用。因为 当具有a时,处理将转移到Out-Host cmdlet 准备好显示的完整页面,在其中位于其前面的cmdlet 管道暂停操作,直到输出的下一页可用。 如果使用Windows任务管理器监视CPU,则可以看到此信息 和Windows PowerShell使用内存。
运行以下命令:
Get-ChildItem C:\Windows -Recurse
。 将CPU和内存使用情况与此命令进行比较:Get-ChildItem C:\Windows -Recurse | Out-Host -Paging
。
在Get-ChildItem
上使用c:\
的基准(大约179516个文件,不是数百万,但足够好):
运行$a = gci c:\ -recurse
(然后执行$a.count
)后的内存使用情况为527,332K
。
运行gci c:\ -recurse | measure-object
后的内存使用量为59,452K
,并且从未超过80,000K
。
(内存 - 私人工作集 - 来自TaskManager,查看powershell.exe
进程的内存。最初,它大概是22,000K
。)
我还尝试了两百万个文件(创建它们需要一段时间!)
类似的实验:
运行$a = gci c:\ -recurse
(然后执行$a.count
)后的内存使用情况为2,808,508K
。
运行gci c:\ -recurse | measure-object
时的内存使用量为308,060K
,并且从未超过400,000K
。完成后,必须执行[GC]::Collect()
才能返回22,000K
级别。
我仍然相信Get-ChildItem
和流水线技术可以为你提供出色的内存改进,即使对于数百万个文件也是如此。
答案 1 :(得分:8)
Get-ChildItem
可能会读取目录中的所有条目,然后开始将它们推送到管道。如果Get-ChildItem
不能正常工作,请尝试切换到.NET 4.0并使用EnumerateFiles
和EnumeratedDirectories
:
function Get-HugeDirStats($directory) {
function go($dir, $stats)
{
foreach ($f in [system.io.Directory]::EnumerateFiles($dir))
{
$stats.Count++
$stats.Size += (New-Object io.FileInfo $f).Length
}
foreach ($d in [system.io.directory]::EnumerateDirectories($dir))
{
go $d $stats
}
}
$statistics = New-Object PsObject -Property @{Count = 0; Size = [long]0 }
go $directory $statistics
$statistics
}
#example
$stats = Get-HugeDirStats c:\windows
这里最昂贵的部分是New-Object io.FileInfo $f
部分,因为EnumerateFiles
只返回文件名。因此,如果只有文件数量足够,您可以对该行进行注释。
请参阅堆叠溢出问题 How can I run PowerShell with the .NET 4 runtime? 学习如何使用.NET 4.0。
您也可以使用速度很快的普通旧方法,但要读取目录中的所有文件。所以这取决于你的需求,试试吧。后来对所有方法进行了比较。
function Get-HugeDirStats2($directory) {
function go($dir, $stats)
{
foreach ($f in $dir.GetFiles())
{
$stats.Count++
$stats.Size += $f.Length
}
foreach ($d in $dir.GetDirectories())
{
go $d $stats
}
}
$statistics = New-Object PsObject -Property @{Count = 0; Size = [long]0 }
go (new-object IO.DirectoryInfo $directory) $statistics
$statistics
}
<强>比较强>:
Measure-Command { $stats = Get-HugeDirStats c:\windows }
Measure-Command { $stats = Get-HugeDirStats2 c:\windows }
Measure-Command { Get-ChildItem c:\windows -recurse | Measure-Object -property length -sum }
TotalSeconds : 64,2217378
...
TotalSeconds : 12,5851008
...
TotalSeconds : 20,4329362
...
@manojlds:流水线是一个基本概念。但作为一个概念,它与提供者无关。文件系统提供程序依赖于没有惰性评估功能(〜枚举器)的.NET实现(.NET 2.0)。检查一下你自己。
答案 2 :(得分:0)
以下功能非常酷,可以快速计算文件夹的大小,但它并不总是有效(特别是在存在权限问题或文件夹路径太长时)。
Function sizeFolder($path) # Return the size in MB.
{
$objFSO = New-Object -com Scripting.FileSystemObject
("{0:N2}" -f (($objFSO.GetFolder($path).Size) / 1MB))
}