使用PowerShell计算文件夹深度 [英] Counting Folder Depth with PowerShell
问题描述
1.代码说明 别名其打算如何工作
用户在PowerShell中输入目录的路径.代码检查声明的目录中的任何文件夹是否根本不包含任何数据.如果是这样,任何空文件夹的路径都会在提示中显示给用户,并最终从系统中删除.
User enters a path to a directory in PowerShell. Code checks if any folder within the declared directory contains no data at all. If so, the path of any empty folder will be shown on the prompt to the user and eventually removed from the system.
2.问题 使我正在苦苦挣扎的
我刚刚编写的代码没有像我期望的那样计算文件夹层次结构的深度(输出表中的列为空白).除此之外,该程序还可以正常工作-我仍然需要解决以下问题:我的代码首先删除了空的父目录,然后又删除了子目录,这当然会在PowerShell中导致错误;例如,以
The code I just wrote doesn't count the depth of a folder hierarchy as I would expect (the column in the output table is blank). Besides that, the program works okay - I've still got to fix the issue where my code removes empty parent directories first and child directories later, which of course will cause an error in PowerShell; for instance, take
C:\Users\JohnMiller\Desktop\Homework
其中家庭作业
由 Homework \ Math \ School Project
和 Homework \ Computer Science \ PowerShell Code
组成.请注意,除 PowerShell Code
(包含此脚本的文件夹)外,所有目录均应为空.(附带说明:当文件夹中没有文件时,该文件夹被认为是空的.至少这是我的代码目前基于的文件夹.)
where Homework
consists of Homework\Math\School Project
and Homework\Computer Science\PowerShell Code
. Note that all directories are supposed to be empty with the exception of PowerShell Code
, the folder containing this script. (Side note: A folder is considered empty when no file dwells inside. At least that's what my code is based on for now.)
3.代码
# Delete all empty (sub)folders in [$path]
[Console]::WriteLine("`n>> Start script for deleting all empty (sub)folders.")
$path = Read-Host -prompt ">> Specify a path"
if (test-path $path)
{
$allFolders = Get-ChildItem $path -recurse | Where {$_.PSisContainer -eq $True}
$allEmptyFolders = $allFolders | Where-Object {$_.GetFiles().Count -eq 0}
$allEmptyFolders | Select-Object FullName,@{Name = "FolderDepth"; Expression = {$_.DirectoryName.Split('\').Count}} | Sort-Object -descending FolderDepth,FullName
[Console]::WriteLine("`n>> Do you want do remove all these directories? Validate with [True] or [False].") #'#
$answer = Read-Host -prompt ">> Answer"
if ([System.Convert]::ToBoolean($answer) -eq $True)
{
$allEmptyFolders | Remove-Item -force -recurse
}
else
{
[Console]::WriteLine(">> Termination confirmed.`n")
exit
}
}
else
{
[Console]::WriteLine(">> ERROR: [$($path)] is an invalid directory. Program terminates.`n")
exit
}
推荐答案
深度计数问题:
您的代码在传递给 Select-Object
的计算所得属性中引用了 .DirectoryName
属性,但 [System.IO.DirectoryInfo]
Get-ChildItem
输出的实例不具有此类属性.使用 .FullName
属性代替:
The depth-count problem:
Your code references a .DirectoryName
property in the calculated property passed to Select-Object
, but the [System.IO.DirectoryInfo]
instances output by Get-ChildItem
have no such property. Use the .FullName
property instead:
$allEmptyFolders |
Select-Object FullName,@{Name='FolderDepth'; Expression={$_.FullName.Split('\').Count}} |
Sort-Object -descending FolderDepth,FullName
消除嵌套的空子文件夹:
以一个简单的例子来概括您的问题:
Eliminating nested empty subfolders:
To recap your problem with a simple example:
如果 c:\ foo
为空(无文件),但子目录为空. c:\ foo \ bar
,您的代码会同时输出它们 ,如果您先删除 c:\ foo
,则删除 c:\ foo \ bar
接下来失败(因为删除 c:\ foo
也删除了 c:\ foo \ bar
).
If c:\foo
is empty (no files) but has empty subdir. c:\foo\bar
, your code outputs them both, and if you then delete c:\foo
first, deleting c:\foo\bar
next fails (because deleting c:\foo
also removed c:\foo\bar
).
如果消除所有嵌套的空子目录.在前面,您不仅可以整理呈现给用户的内容,还可以安全地迭代输出并逐一删除.
If you eliminate all nested empty subdirs. up front, you not only declutter what you present to the user, but you can then safely iterative of the output and delete one by one.
使用您的方法,您需要第二步才能消除嵌套的空目录.但这是深度优先的递归函数,它忽略了嵌套的空文件夹.要使其与隐藏文件的行为相同,请传递 -Force
.
With your approach you'd need a 2nd step to eliminate the nested empty dirs., but here's a depth-first recursive function that omits nested empty folders. To make it behave the same way as your code with respect to hidden files, pass -Force
.
function Get-RecursivelyEmptyDirectories {
[cmdletbinding()]
param(
[string] $LiteralPath = '.',
[switch] $Force,
[switch] $DoNotValidatePath
)
$ErrorActionPreference = 'Stop'
if (-not $DoNotValidatePath) {
$dir = Get-Item -LiteralPath $LiteralPath
if (-not $dir.PSIsContainer) { Throw "Not a directory path: $LiteralPath" }
$LiteralPath = $dir.FullName
}
$haveFiles = [bool] (Get-ChildItem -LiteralPath $LiteralPath -File -Force:$Force | Select-Object -First 1)
$emptyChildDirCount = 0
$emptySubdirs = $null
if ($childDirs = Get-ChildItem -LiteralPath $LiteralPath -Directory -Force:$Force) {
$emptySubDirs = New-Object System.Collections.ArrayList
foreach($childDir in $childDirs) {
if ($childDir.LinkType -eq 'SymbolicLink') {
Write-Verbose "Ignoring symlink: $LiteralPath"
} else {
Write-Verbose "About to recurse on $($childDir.FullName)..."
try { # If .AddRange() fails due to exceeding the array list's capacity, we must fail too.
$emptySubDirs.AddRange(@(Get-RecursivelyEmptyDirectories -DoNotValidatePath -LiteralPath $childDir.FullName -Force:$Force))
} catch {
Throw
}
# If the last entry added is the child dir. at hand, that child dir.
# is by definition itself empty.
if ($emptySubDirs[-1] -eq $childDir.FullName) { ++$emptyChildDirCount }
}
} # foreach ($childDir ...
} # if ($childDirs = ...)
if (-not $haveFiles -and $emptyChildDirCount -eq $childDirs.Count) {
# There are no child files and all child dirs., if any, are themselves
# empty, so we only output the input path at hand, as the highest
# directory in this subtree that is empty (save for empty descendants).
$LiteralPath
} else {
# This directory is not itself empty, so output the (highest-level)
# descendants that are empty.
$emptySubDirs
}
}
有关您的代码的提示:
Tips regarding your code:
-
Get-ChildItem -Directory
在PSv3 +中可用,它不仅比Get-ChildItem | |更短,而且效率更高...其中{$ _.PSisContainer -eq $ True}
.
Get-ChildItem -Directory
is available in PSv3+, which is not only shorter but also more efficient thanGet-ChildItem | .. Where { $_.PSisContainer -eq $True }
.
使用 Write-Host
代替 [Console] :: WriteLine
[System.Convert] :: ToBoolean($ answer)
仅适用于不区分文化的字符串文字'True'
和'False
( [bool] :: TrueString
和 [bool] :: FalseString
,尽管允许大小写变化以及前导和尾随空白).
[System.Convert]::ToBoolean($answer)
only works with the culture-invariant string literals 'True'
and 'False'
([bool]::TrueString
and [bool]::FalseString
, although case variations and leading and trailing whitespace are allowed).
这篇关于使用PowerShell计算文件夹深度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!