[英]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"
}
dir
是Get-ChildItem
的別名,顧名思義,它獲取指定位置的子元素,但不是代表該位置本身的項目; 為此,您需要Get-Item
。 看起來您正在powershell
目錄中執行該管道,一旦?{ $_.PSIsContainer }
過濾掉所有子文件, %{... }
在唯一的子目錄linux
上執行。
你能做的是……
通過這種方法,每個容器和葉子都被恰好“接觸”(枚舉)一次。
這使用[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 個對象,如下所示...
名稱 | 容器計數 | 葉數 | 總數 |
---|---|---|---|
powershell | 1個 | 3個 | 4個 |
linux | 0 | 2個 | 2個 |
...盡管它們的順序 output 取決於算法。 然后,您可以使用Select-Object
、 Sort-Object
、 Where-Object
等標准 cmdlet 操作和查看它們。
此外,由於上面的代碼是以與提供者無關的方式編寫的(“容器”和“葉子”與“目錄”和“文件”相比),它也適用於其他類型的PSDrive
; 例如,嘗試運行...
cd 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\'
...在上面的代碼之前看到它枚舉和計算注冊表項(雖然不是值,它們不是項目而是項目屬性)。
這是一個簡單的 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))
}
跑步時...
.\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.