簡體   English   中英

一個 PowerShell 腳本來查找包含數百萬個文件的文件夾的文件大小和文件數?

[英]A PowerShell script to find the file size and file count of a folder with millions of files?

該腳本的目的如下:

  1. 打印在目錄中遞歸找到的文件數(省略文件夾本身)
  2. 打印目錄的總和文件大小
  3. 不會因為大量使用 memory 導致計算機崩潰。

到目前為止(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)的問題,因為當你使用 ZF7B44CFFAFD5C52223D5498196C8A2E7BZ 時,每個 object 在它們可用並且不占用太多 ZCD69B4957F06CD8298D7ZBF3D61 時沿着管道傳遞處理數百萬個文件(盡管這需要時間)。

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 管道中給出了一個很好的例子。

引用它:

每當您希望緩慢顯示冗長的 output 時,Out-Host -Paging 命令是一個有用的管道元素。 如果操作非常占用 CPU,則它特別有用。 由於處理會在准備好顯示完整頁面時轉移到 Out-Host cmdlet,因此管道中位於其前面的 cmdlet 會暫停操作,直到 output 的下一頁可用。 如果您使用 Windows 任務管理器來監控 CPU 和 memory 使用 Windows Z3D265B4E03BEEEF0DDF17881FA0,您可以看到這一點

運行以下命令: Get-ChildItem C:\Windows -Recurse 將 CPU 和 memory 用法與以下命令進行比較: Get-ChildItem C:\Windows -Recurse | Out-Host -Paging Get-ChildItem C:\Windows -Recurse | Out-Host -Paging

在 c:\ 上使用Get-ChildItemc:\ (大約 179516 個文件,不是數百萬,但足夠好):

運行$a = gci c:\ -recurse (然后執行$a.count )后的 Memory 使用量為527,332K

Memory 運行 gci 后的用法gci c:\ -recurse | measure-object gci c:\ -recurse | measure-object59,452K並且從未超過80,000K左右。

(內存 - 私有工作集 - 來自 TaskManager,看到powershell.exe進程的 memory。最初,它大約是22,000K 。)

我還嘗試了兩百萬個文件(我花了一些時間來創建它們!)

類似實驗:

運行$a = gci c:\ -recurse (然后執行$a.count )后的 Memory 使用量為2,808,508K

運行 gci 時 Memory 的使用gci c:\ -recurse | measure-object gci c:\ -recurse | measure-object308,060K並且從未超過400,000K左右。 完成后,它必須執行[GC]::Collect()才能返回到22,000K級別。

我仍然相信Get-ChildItem和流水線可以為您帶來出色的 memory 改進,即使對於數百萬個文件也是如此。

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

這里最昂貴的部分是帶有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 很酷,計算文件夾大小的速度很快,但它並不總是有效(尤其是當有權限問題或文件夾路徑太長時)。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM