使用 PowerShell 计算文件夹深度

Counting Folder Depth with PowerShell

1.代码说明 别名它是如何工作的

用户在 PowerShell 中输入目录路径。代码检查声明目录中的任何文件夹是否根本不包含任何数据。如果是这样,任何空文件夹的路径都会在提示中显示给用户,并最终从系统中删除。


2。问题 别名是我正在努力解决的问题

我刚刚编写的代码没有像我预期的那样计算文件夹层次结构的深度(输出 table 中的列为空白)。除此之外,该程序运行良好——我仍然需要解决我的代码先删除空父目录然后再​​删除子目录的问题,这当然会导致 PowerShell 出错;例如,取

C:\Users\JohnMiller\Desktop\Homework

其中 HomeworkHomework\Math\School ProjectHomework\Computer Science\PowerShell Code 组成。请注意,所有目录都应该是空的,但 PowerShell Code 除外,该文件夹包含此脚本。 (旁注:当文件夹中没有文件时,该文件夹被认为是空的。至少我的代码目前是基于此的。)


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 属性,但是 Get-ChildItem 输出的 [System.IO.DirectoryInfo] 个实例没有这样的属性。使用 .FullName 属性 代替:

$allEmptyFolders | 
  Select-Object FullName,@{Name='FolderDepth'; Expression={$_.FullName.Split('\').Count}} |
    Sort-Object -descending FolderDepth,FullName

消除嵌套的空子文件夹:

用一个简单的例子来概括你的问题:

如果 c:\foo 为空(无文件)但子目录为空。 c:\foo\bar,你的代码输出它们 both,如果你先删除 c:\foo,接下来删除 c:\foo\bar 会失败(因为删除 c:\foo也删除了 c:\foo\bar).

如果消除所有嵌套的空子目录。在前面,您不仅可以整理呈现给用户的内容,还可以安全地迭代输出并一一删除。

使用您的方法,您需要第二步来消除嵌套的空目录。但这里有一个深度优先递归函数,它忽略了嵌套的空文件夹。要使其在隐藏文件方面的行为与您的代码相同,请传递 -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
  }

}

关于您的代码的提示:

  • Get-ChildItem -Directory在PSv3+中可用,比Get-ChildItem | .. Where { $_.PSisContainer -eq $True }.

  • 不仅更短而且效率更高
  • 使用Write-Host代替[Console]::WriteLine

  • [System.Convert]::ToBoolean($answer) 仅适用于文化不变的字符串文字 'True''False'[bool]::TrueString[bool]::FalseString,尽管大小写变体和前导和尾随空格是允许的)。