使用PowerShell计算文件夹深度 [英] Counting Folder Depth with PowerShell

查看:55
本文介绍了使用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 than Get-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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆