PowerShell脚本,用于查找包含数百万个文件的文件夹的文件大小和文件数?

Ste*_*ood 21 powershell sum

该脚本的目的如下:

  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"
Run Code Online (Sandbox Code Playgroud)

但是,在包含数百万个文件的文件夹中,$colitems变量从数百万个文件的信息集合中变得如此庞大,这使得系统不稳定.有没有更有效的方法来绘制和存储这些信息?

man*_*lds 36

如果你使用流和流水线,你应该减少(3)很多问题,因为当你流,每个对象在它们可用时传递沿着管道,并且不占用太多内存,你应该能够处理数百万个文件(虽然需要时间).

Get-ChildItem $directory -recurse | Measure-Object -property length -sum
Run Code Online (Sandbox Code Playgroud)

我不相信@ 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任务管理器监视Windows PowerShell的CPU和内存使用情况,则可以看到此信息.

运行以下命令:Get-ChildItem C:\Windows -Recurse.将CPU和内存使用情况与此命令进行比较:Get-ChildItem C:\Windows -Recurse | Out-Host -Paging.

基准上使用Get-ChildItemc:\(约179516文件,而不是milions,但不够好):

运行$a = gci c:\ -recurse(然后执行$a.count)后的内存使用情况是527,332K.

运行后的内存使用量gci c:\ -recurse | measure-object59,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即使对于数百万个文件,流水线操作也可以为您带来出色的内存改进.

  • @manojlds 您可能希望向 Get-ChildItem 添加 `-force` 标志,以便它读取系统和隐藏文件(我讨厌那个“功能”)。 (2认同)

ste*_*tej 10

Get-ChildItem可能会读取目录中的所有条目,然后开始将它们推送到管道.如果Get-ChildItem不能正常工作,请尝试切换到.NET 4.0并使用EnumerateFilesEnumeratedDirectories:

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
Run Code Online (Sandbox Code Playgroud)

这里最昂贵的部分是一个New-Object io.FileInfo $f,因为EnumerateFiles只返回文件名.因此,如果只有文件数量就足够了,您可以对该行进行注释.

请参阅Stack Overflow问题如何使用.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
}
Run Code Online (Sandbox Code Playgroud)

比较:

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
...
Run Code Online (Sandbox Code Playgroud)

@manojlds:流水线是一个基本概念.但作为一个概念,它与提供者无关.文件系统提供程序依赖于没有惰性评估功能(〜枚举器)的.NET实现(.NET 2.0).检查一下你自己.