该脚本的目的如下:
到目前为止(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
变量由于收集了数百万个文件的信息而变得如此庞大,以至于导致系统不稳定。有没有更有效的方法来绘制和存储这些信息?
如果您使用流式传输和管道传输,则应该大大减少(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
不会有不同的行为。了解 Windows PowerShell 管道中给出了一个很好的示例。 引自:
Out-Host -Paging 命令是一个有用的管道元素,只要您 有冗长的输出,您希望缓慢显示。这是 如果操作非常占用 CPU 资源,则特别有用。因为 当 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
。(内存 - 私有工作集 - 来自任务管理器,查看
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
和流水线技术可以为您带来巨大的内存改进,即使对于数百万个文件也是如此。
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
仅返回文件名。因此,如果仅文件数就足够了,您可以注释该行。请参阅堆栈溢出问题
如何使用 .NET 4 运行时运行 PowerShell? 学习如何使用 .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)。自己检查一下。
Function sizeFolder($path) # Return the size in MB.
{
$objFSO = New-Object -com Scripting.FileSystemObject
("{0:N2}" -f (($objFSO.GetFolder($path).Size) / 1MB))
}
) .
function com { param([Parameter(ValueFromPipeline=$True)]
[int64]$number)
process { '{0:n0}' -f $number } }
function dus($dir=".") {
get-childitem -force -directory $dir -erroraction silentlycontinue `
-attributes !reparsepoint |
foreach { $f = $_
get-childitem -force -r $_.FullName -attributes !reparsepoint -ea 0 |
measure-object -property length -sum -erroraction silentlycontinue |
select @{Name="Name";Expression={$f}},Sum} | sort Sum |
select name,@{n='Sum';e={$_.sum | com}}
}
function siz() {
ls -file -force | select name,length |
sort length |
select name,@{n='Length';e={$_.length | com}}
}
dus
name sum
---- ---
Documents 2,790,100,862
AppData 20,571,019,655
Downloads 25,564,270,200
siz
name length
---- ------
ntuser.dat.LOG2 3,691,520
ntuser.dat.LOG1 3,698,688
NTUSER.DAT 14,680,064