簡體   English   中英

如何計算基目錄和每個后代目錄中的目錄和文件數?

[英]How can I get a count of directories and files in a base directory and each descendant directory?

我正在計算路徑中的文件和文件夾,該路徑包含多個文件和文件夾。

我正在使用這個:

dir -recurse |  ?{ $_.PSIsContainer } | %{ Write-Host $_.FullName (dir $_.FullName | Measure-Object).Count }

問題是我想知道計數是否包含子文件夾。 另外,上面的命令沒有給我基本文件夾的文件數。

例如

  • powershell\
    • Copy_files.ps1
    • powershell.txt
    • sortbydate.ps1
    • linux\
      • New Text Document (2).txt
      • New Text Document.txt

對於上述文件結構,我得到:

linux 2

雖然我想要:

Powershell 3 ( + 1 directory )
linux 2

這是我的第二次嘗試 - 有點丑,但似乎可以完成這項工作。

Get-ChildItem -Recurse -Directory | ForEach-Object {
    [Object[]]$Dirs = (Get-ChildItem -Path $_.FullName -Directory)
    $DirText = if($Dirs.Length -gt 0) {" ( + $($Dirs.Length) directory )"} else {''}
    [Object[]]$Files = (Get-ChildItem -Path $_.FullName -File)
    "$(' ' * ($_.FullName -split '\\').Length)$($_.Name) $($Files.Length)$DirText"
}

dirGet-ChildItem的別名,顧名思義,它獲取指定位置的元素,但不是代表該位置本身的項目; 為此,您需要Get-Item 看起來您正在powershell目錄中執行該管道,一旦?{ $_.PSIsContainer }過濾掉所有子文件, %{... }在唯一的子目錄linux上執行。

基本實現

你能做的是……

  1. 從某個基本位置(當前目錄)開始......
  2. 枚舉當前位置的子級,沿途跟蹤容器(目錄)和葉(文件)的數量
  3. 遇到容器時,要么立即開始處理,要么存儲起來待會處理
  4. 枚舉完所有子項后,output 當前容器的結果

通過這種方法,每個容器和葉子都被恰好“接觸”(枚舉)一次。

迭代實現

這使用[Queue[]]容器來處理上述步驟; 也可以使用[Stack[]] 由於遍歷從一個容器開始處理(當前位置),如果還有容器要遍歷,則使用do { } while ()循環繼續執行。

# Stores the current container being processed; initialize to the current location
$current = Get-Item -Path '.' -Force

# Stores containers that have been encountered but not yet processed
# Using a Queue[PSObject] with Enqueue()/Dequeue() results in breadth-first traversal
# Using a Stack[PSObject] with    Push()/Pop()     results in   depth-first traversal
$pendingContainers = New-Object -TypeName 'System.Collections.Generic.Queue[PSObject]'

do
{
    $containerCount = 0
    $leafCount = 0

    foreach ($child in Get-ChildItem -LiteralPath $current.PSPath -Force)
    {
        if ($child.PSIsContainer)
        {
            $containerCount++

            # Store the child container for later processing
            $pendingContainers.Enqueue($child)
        }
        else
        {
            $leafCount++
        }
    }

    # Write-Output is superfluous here, though makes it explicit that its input will be sent down the pipeline
    Write-Output -InputObject (
        [PSCustomObject] @{
            Name           = $current.PSChildName
            ContainerCount = $containerCount
            LeafCount      = $leafCount
            TotalCount     = $containerCount + $leafCount
        }
    )
}
# Assign the next container to $current, if available; otherwise, exit the loop
# The second operand to -and works because assigning a non-$null value evaluates to $true
while ($pendingContainers.Count -gt 0 -and ($current = $pendingContainers.Dequeue()))
# For PowerShell (Core) 6+: while ($pendingContainers.TryDequeue([Ref] $current))

使用Group-Object進行計數

您可以使用Group-Object cmdlet構建一個[Hashtable] ,其鍵控是子項是容器 ( $true ) 還是葉 ( $false )。 這以降低效率和增加 memory 的使用為代價稍微簡化了上面的代碼。 請注意,這可以類似地修改以用於下一節中的遞歸實現。

# Stores the current container being processed; initialize to the current location
$current = Get-Item -Path '.' -Force

# Stores containers that have been encountered but not yet processed
# Using a Queue[PSObject] with Enqueue()/Dequeue() results in breadth-first traversal
# Using a Stack[PSObject] with    Push()/Pop()     results in   depth-first traversal
$pendingContainers = New-Object -TypeName 'System.Collections.Generic.Queue[PSObject]'

do
{
    $childrenByIsContainer = Get-ChildItem -LiteralPath $current.PSPath -Force |
        Group-Object -AsHashTable -Property 'PSIsContainer'
    $containerCount = $childrenByIsContainer[$true].Count
    $leafCount = $childrenByIsContainer[$false].Count

    foreach ($childContainer in $childrenByIsContainer[$true])
    {
        # Store the child container for later processing
        $pendingContainers.Enqueue($childContainer)
    }

    # Write-Output is superfluous here, though makes it explicit that its input will be sent down the pipeline
    Write-Output -InputObject (
        [PSCustomObject] @{
            Name           = $current.PSChildName
            ContainerCount = $containerCount
            LeafCount      = $leafCount
            TotalCount     = $containerCount + $leafCount
        }
    )
}
# Assign the next container to $current, if available; otherwise, exit the loop
# The second operand to -and works because assigning a non-$null value evaluates to $true
while ($pendingContainers.Count -gt 0 -and ($current = $pendingContainers.Dequeue()))
# For PowerShell (Core) 6+: while ($pendingContainers.TryDequeue([Ref] $current))

遞歸實現

上面的迭代實現可能更自然地寫成遞歸function。代碼更短一些並且可能更容易理解,盡管一個缺點是在枚舉其所有后代之前,給定容器的結果不會是 output,這可能會導致大型層次結構中出現明顯的延遲。

function MeasureContainer($container)
{
    $containerCount = 0
    $leafCount = 0

    foreach ($child in Get-ChildItem -LiteralPath $container.PSPath -Force)
    {
        if ($child.PSIsContainer)
        {
            $containerCount++

            Write-Output -InputObject (
                MeasureContainer $child
            )
        }
        else
        {
            $leafCount++
        }
    }

    Write-Output -InputObject (
        [PSCustomObject] @{
            Name           = $container.PSChildName
            ContainerCount = $containerCount
            LeafCount      = $leafCount
            TotalCount     = $containerCount + $leafCount
        }
    )
}

MeasureContainer (Get-Item -Path '.' -Force)

為了開始遞歸,在最后調用MeasureContainer並傳遞基礎容器,它和以前一樣是當前位置。

Output

執行上述任何代碼塊都會產生 output 個對象,如下所示...

名稱 容器計數 葉數 總數
powershell 1個 3個 4個
linux 0 2個 2個

...盡管它們的順序 output 取決於算法。 然后,您可以使用Select-ObjectSort-ObjectWhere-Object等標准 cmdlet 操作和查看它們。

此外,由於上面的代碼是以與提供者無關的方式編寫的(“容器”和“葉子”與“目錄”和“文件”相比),它也適用於其他類型的PSDrive 例如,嘗試運行...

cd 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\'

...在上面的代碼之前看到它枚舉和計算注冊表項(雖然不是值,它們不是項目而是項目屬性)。

Cmdlet 實現

這是一個簡單的 cmdlet,它通過在 output object 中包含每個容器的絕對路徑、相對路徑和深度來增強上述迭代代碼,並允許限制遍歷的最大深度。

[CmdletBinding()]
param(
    [String] $Path = '.',                  # Default to current location
    [ValidateRange(-1, [Int32]::MaxValue)]
    [Int32] $MaxDepth = -1                 # Default to unlimited depth
)

# Stores the current container being processed; initialize to the base container from parameters
[PSObject] $current = [PSCustomObject] @{
    Container = Get-Item -LiteralPath $Path -Force -ErrorAction SilentlyContinue
    Depth = 0
}

if ($null -eq $current.Container)
{
    Write-Error -Message "The object referred to by the base path ""$Path"" could not be found."
}
elseif (-not $current.Container.PSIsContainer)
{
    Write-Error -Message "The object referred to by the base path ""$Path"" is not a container."
}
else
{
    # Stores containers that have been encountered but not yet processed
    # Using a Queue[PSObject] with Enqueue()/Dequeue() results in breadth-first traversal
    # Using a Stack[PSObject] with    Push()/Pop()     results in   depth-first traversal
    [System.Collections.Generic.Queue[PSObject]] $pendingContainers = 
        New-Object -TypeName 'System.Collections.Generic.Queue[PSObject]'

    do
    {
        [Int32] $containerCount = 0
        [Int32] $leafCount = 0
    
        #TODO: Handle errors due to inaccessible children
        foreach ($child in Get-ChildItem -LiteralPath $current.Container.PSPath -Force)
        {
            if ($child.PSIsContainer)
            {
                #TODO: Detect and exit directory cycles caused by junctions or symbolic links
                #      See [System.IO.FileAttributes]::ReparsePoint

                $containerCount++
    
                # If the current depth is within the depth limit, or there is no depth limit...
                if ($current.Depth -lt $MaxDepth -or $MaxDepth -eq -1)
                {
                    # Store the child container for later processing
                    $pendingContainers.Enqueue(
                        [PSCustomObject] @{
                            Container = $child
                            Depth = $current.Depth + 1
                        }
                    )
                }
            }
            else
            {
                $leafCount++
            }
        }

        # Write-Output is superfluous here, though makes it explicit that its input will be sent down the pipeline
        Write-Output -InputObject (
            [PSCustomObject] @{
                                 # Display a "friendly" provider-specific path instead
                AbsolutePath   = Convert-Path -LiteralPath $current.Container.PSPath
                RelativePath   = if ($current.Depth -eq 0) {
                                     # Resolve-Path ... -Relative returns a path prefixed with ".." when
                                     # passed the current location; substitute a less roundabout value instead
                                     '.'
                                 } else {
                                     # Resolve-Path ... -Relative returns a path relative to the current
                                     # location and doesn't allow another base location to be specified, so
                                     # the location must changed before and reverted after resolution.  Bleh.
                                     Push-Location -LiteralPath $Path
                                     try
                                     {
                                         Resolve-Path -LiteralPath $current.Container.PSPath -Relative
                                     }
                                     finally
                                     {
                                         Pop-Location
                                     }
                                  }
                Name           = $current.Container.PSChildName
                Depth          = $current.Depth
                ContainerCount = $containerCount
                LeafCount      = $leafCount
                TotalCount     = $containerCount + $leafCount
            }
        )
    }
    # Assign the next container to $current, if available; otherwise, exit the loop
    # The second operand to -and works because assigning a non-$null value evaluates to $true
    while ($pendingContainers.Count -gt 0 -and ($current = $pendingContainers.Dequeue()))
    # For PowerShell (Core) 6+: while ($pendingContainers.TryDequeue([Ref] $current))
}

Output

跑步時...

.\SO71470092.ps1 -Path $PSHOME | Select-Object -First 25

...我在 Windows Powershell 5.1.19041.1320 ...

絕對路徑 相對路徑 名稱 深度 容器計數 葉數 總數
C:\Windows\System32\WindowsPowerShell\v1.0 . v1.0 0 6個 29 35
C:\Windows\System32\WindowsPowerShell\v1.0\en .\zh 1個 0 1個 1個
C:\Windows\System32\WindowsPowerShell\v1.0\en-US .\zh-CN 英文 1個 0 271 271
C:\Windows\System32\WindowsPowerShell\v1.0\Examples 。\例子 例子 1個 0 1個 1個
C:\Windows\System32\WindowsPowerShell\v1.0\Modules .\模塊 模塊 1個 75 0 75
C:\Windows\System32\WindowsPowerShell\v1.0\Schemas .\模式 圖式 1個 1個 0 1個
C:\Windows\System32\WindowsPowerShell\v1.0\SessionConfig .\會話配置 會話配置 1個 0 0 0
C:\Windows\System32\WindowsPowerShell\v1.0\Modules\AppBackgroundTask .\Modules\AppBackgroundTask 應用后台任務 2個 1個 5個 6個
C:\Windows\System32\WindowsPowerShell\v1.0\Modules\AppLocker .\Modules\AppLocker 應用鎖 2個 1個 2個 3個
C:\Windows\System32\WindowsPowerShell\v1.0\Modules\AppvClient .\Modules\AppvClient 應用客戶端 2個 2個 7 9
C:\Windows\System32\WindowsPowerShell\v1.0\Modules\Appx .\Modules\Appx 附錄 2個 1個 4個 5個
C:\Windows\System32\WindowsPowerShell\v1.0\Modules\AssignedAccess .\Modules\AssignedAccess 分配的訪問 2個 1個 3個 4個
C:\Windows\System32\WindowsPowerShell\v1.0\Modules\BitLocker .\模塊\BitLocker 比特鎖 2個 1個 5個 6個
C:\Windows\System32\WindowsPowerShell\v1.0\Modules\BitsTransfer .\Modules\BitsTransfer 比特傳輸 2個 1個 4個 5個
C:\Windows\System32\WindowsPowerShell\v1.0\Modules\BranchCache .\Modules\BranchCache 分支緩存 2個 1個 13 14
C:\Windows\System32\WindowsPowerShell\v1.0\Modules\CimCmdlets .\Modules\CimCmdlets CimCmdlets 2個 1個 2個 3個
C:\Windows\System32\WindowsPowerShell\v1.0\Modules\ConfigCI .\Modules\ConfigCI 配置CI 2個 1個 2個 3個
C:\Windows\System32\WindowsPowerShell\v1.0\Modules\Defender .\模塊\后衛 后衛 2個 1個 10 11
C:\Windows\System32\WindowsPowerShell\v1.0\Modules\DeliveryOptimization .\Modules\DeliveryOptimization 交付優化 2個 0 4個 4個
C:\Windows\System32\WindowsPowerShell\v1.0\Modules\DirectAccessClientComponents .\Modules\DirectAccessClientComponents DirectAccessClientComponents 2個 1個 8個 9
C:\Windows\System32\WindowsPowerShell\v1.0\Modules\Dism .\模塊\Dism 歧視 2個 2個 6個 8個
C:\Windows\System32\WindowsPowerShell\v1.0\Modules\DnsClient .\模塊\DnsClient 客戶端 2個 1個 16 17
C:\Windows\System32\WindowsPowerShell\v1.0\Modules\EventTracingManagement .\Modules\EventTracingManagement 事件追蹤管理 2個 1個 10 11
C:\Windows\System32\WindowsPowerShell\v1.0\Modules\International .\模塊\國際 國際的 2個 1個 2個 3個
C:\Windows\System32\WindowsPowerShell\v1.0\Modules\iSCSI .\模塊\iSCSI iSCSI 2個 1個 6個 7

暫無
暫無

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

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