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

用于查找包含数百万文件的文件夹的文件大小和文件计数的PowerShell脚本?,powershell,sum,Powershell,Sum,脚本的目的如下: 打印在目录中递归找到的文件数 (省略文件夹本身) 打印目录的总文件大小 不会因为大量内存使用而导致计算机崩溃 到目前为止(3)是困难的部分 以下是我到目前为止编写和测试的内容。这在包含100个甚至1000个文件的文件夹上非常有效: $hostname=hostname $directory = "foo" $dteCurrentDate = Get-Date –f "yyyy/MM/dd" $FolderItems = Get-ChildItem $directory -re

脚本的目的如下:

  • 打印在目录中递归找到的文件数 (省略文件夹本身)
  • 打印目录的总文件大小
  • 不会因为大量内存使用而导致计算机崩溃
  • 到目前为止(3)是困难的部分

    以下是我到目前为止编写和测试的内容。这在包含100个甚至1000个文件的文件夹上非常有效:

    $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)很多问题,因为当您流式处理时,每个对象都会在可用时沿管道传递,并且不会占用太多内存,您应该能够处理数百万个文件(尽管这需要时间)

    我不相信@Stej的声明,
    Get ChildItem可能读取目录中的所有条目,然后开始将它们推送到管道中。
    是正确的。管道是PowerShell的一个基本概念(提供cmdlet、脚本等。支持它)。它既可以确保处理后的对象在可用时,也可以仅在需要时,沿管道逐个传递。
    Get ChildItem
    的行为不会有所不同

    中给出了一个很好的例子

    引述:

    无论何时,Out Host-Paging命令都是一个有用的管道元素 有很长的输出,您希望缓慢显示。它是 如果操作非常占用CPU,则特别有用。因为 当Out-Host cmdlet具有 已准备好显示的完整页面,在 管道暂停操作,直到下一页输出可用。 如果使用Windows任务管理器监视CPU,则可以看到这一点 以及Windows PowerShell使用的内存

    运行以下命令:
    getchilditem C:\Windows-Recurse
    。 将CPU和内存使用情况与以下命令进行比较:
    Get ChildItem
    C:\Windows-Recurse | Out主机-分页

    c:\
    上使用
    getchilditem
    作为基准(大约179516个文件,不是百万,但足够好):

    运行
    $a=gci c:\-recurse
    (然后执行
    $a.count
    )后的内存使用量为
    527332k

    运行
    gci c:\-recurse | measure object
    后的内存使用量为
    59452K
    ,并且在
    80000K左右从未超过

    (内存-专用工作集-来自TaskManager,查看
    powershell.exe
    进程的内存。最初,它大约是
    22000k

    我还尝试了200万个文件(创建它们花了我一段时间!)

    类似实验:

    运行
    $a=gci c:\-recurse
    (然后执行
    $a.count
    )后的内存使用量为
    2808508k

    运行
    gci c:\-recurse | measure object
    时的内存使用量为
    308060k
    ,从未超过
    400000k
    。完成后,它必须执行
    [GC]::Collect()
    才能返回到
    22000k
    级别


    我仍然相信
    Get ChildItem
    和管道技术可以为数百万个文件带来巨大的内存改进。

    Get ChildItem
    可能读取目录中的所有条目,然后开始将它们推送到管道。如果
    Get ChildItem
    工作不正常,请尝试切换到.NET 4.0和使用
    枚举文件
    枚举目录

    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
    
    这里最昂贵的部分是带有
    新对象io.FileInfo$f
    的部分,因为
    EnumerateFiles
    只返回文件名。因此,如果只计算文件数就足够了,您可以对该行进行注释

    请参阅堆栈溢出问题 学习如何使用.NET4.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)。请自己检查。

    以下函数非常酷,可以快速计算文件夹的大小,但并不总是有效(尤其是当存在权限问题或文件夹路径过长时)


    一个很好的解决方案是小心地将该线程更改为.Net-您是标准的\v2 PowerShell无法与所有功能(包括远程处理)一起使用。最好复制文件夹并在那里进行更改,只有在需要使用v4时才调用此PS。它们并行工作。@Matt,我在.NET 4.0下运行PowerShell几个月了,没有问题。(更改了配置文件)远程处理工作正常?这很酷。大约3-4个月前,我遇到了一些问题-我想知道我是否使用了最新版本的.net v4。我在Win7Win server 2008和它工作期间不时使用远程处理:)再试一次,我希望它会正常。@stej-作为一名C#dev,我同意你的看法,这就是我在C#中的做法。但我想看看Get childItem是否真的能完成这项工作。你能试试我在更新后的答案中所做的命令并测量内存使用情况吗?
    Get ChildItem
    实际上表现不同。@你可能想添加
    -force
    标志来获取ChildItem,这样它就可以读取系统和隐藏文件(我讨厌这个“功能”)。@stej-I比较了内存使用情况。你能从我更新的答案中尝试同样的方法吗?@man
    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
    ...
    
    Function sizeFolder($path) # Return the size in MB.
    {
        $objFSO = New-Object -com  Scripting.FileSystemObject
        ("{0:N2}" -f (($objFSO.GetFolder($path).Size) / 1MB))
    }