一个 PowerShell 脚本来查找包含数百万个文件的文件夹的文件大小和文件数量?

问题描述 投票:0回答:4

该脚本的目的如下:

  1. 打印在目录中递归找到的文件数 (省略文件夹本身)
  2. 打印目录总和文件大小
  3. 不会因为使用大量内存而导致计算机崩溃。

到目前为止(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
变量由于收集了数百万个文件的信息而变得如此庞大,以至于导致系统不稳定。有没有更有效的方法来绘制和存储这些信息?

powershell sum
4个回答
40
投票

如果您使用流式传输和管道传输,则应该大大减少(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

和流水线技术可以为您带来巨大的内存改进,即使对于数百万个文件也是如此。

    


11
投票
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)。自己检查一下。


1
投票

Function sizeFolder($path) # Return the size in MB. { $objFSO = New-Object -com Scripting.FileSystemObject ("{0:N2}" -f (($objFSO.GetFolder($path).Size) / 1MB)) }



0
投票
https://github.com/jdhitsolutions/PSScriptTools/blob/master/docs/Get-FolderSizeInfo.md

) . 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
	
© www.soinside.com 2019 - 2024. All rights reserved.