【问题标题】:How to delete empty subfolders with PowerShell?如何使用 PowerShell 删除空子文件夹?
【发布时间】:2010-12-07 05:23:42
【问题描述】:

我有一个共享,它是最终用户的“垃圾抽屉”。他们可以根据需要创建文件夹和子文件夹。我需要实现一个脚本来删除创建超过 31 天的文件。

我是从 Powershell 开始的。我需要通过删除现在为空的子文件夹来跟进文件删除脚本。由于子文件夹的嵌套,我需要避免删除一个没有文件的子文件夹,但它下面有一个包含文件的子文件夹。

例如:

  • FILE3a 已经 10 天了。 FILE3 45 天了。
  • 我想清理结构,删除超过 30 天的文件,并删除空子文件夹。
C:\Junk\subfolder1a\subfolder2a\FILE3a

C:\Junk\subfolder1a\subfolder2a\subfolder3a

C:\Junk\subfolder1a\subfolder2B\FILE3b

想要的结果:

  • 删除:FILE3bsubfolder2Bsubfolder3a
  • 离开:subfolder1asubfolder2aFILE3a

我可以递归地清理文件。如何在不删除 subfolder1a 的情况下清理子文件夹? (“垃圾”文件夹将始终保留。)

【问题讨论】:

    标签: file powershell recursion delete-directory


    【解决方案1】:

    我会分两遍执行此操作 - 先删除旧文件,然后删除空目录:

    Get-ChildItem -recurse | Where {!$_.PSIsContainer -and `
    $_.LastWriteTime -lt (get-date).AddDays(-31)} | Remove-Item -whatif
    
    Get-ChildItem -recurse | Where {$_.PSIsContainer -and `
    @(Get-ChildItem -Lit $_.Fullname -r | Where {!$_.PSIsContainer}).Length -eq 0} |
    Remove-Item -recurse -whatif
    

    这种类型的操作演示了第二组命令演示的 PowerShell 中嵌套管道的强大功能。它使用嵌套管道递归地确定任何目录下是否有零个文件。

    【讨论】:

    • 是的,它们非常有用。我希望对于 PoSh 的下一个版本,我们可以省去 Where PSIContainer 测试。如果我可以直接从 Get-ChildItem 请求,那就更好了。 Get-ChildItem -Recurse -Container 或 Get-ChildItem -Recurse -Leaf。对于提供者来说,进行这种过滤也可能是一个非常好的性能优化。一个人可以做梦。 :-)
    • 谢谢!这个解决方案正是我所追求的。递归检查剩余文件的子文件夹让我望而却步——更不用说语法和嵌套了。我是 PowerShell 新手 - '@' 是什么/它是如何工作的?
    • @() 语法确保无论我们没有得到结果 ($null) 还是单个结果或结果数组 - 使用@() 始终是一个包含 0、1 或 N 个项目的数组。这就是我确定结果将具有 Length 属性并且 Length 属性用于数组的方式(与字符串相反,如果结果是单个字符串值,则可能会返回该字符串)。像这样的动态语言可能非常强大,但它们会让你思考。 :-)
    • 删除管道实际上包含一个小错误。我在包含[] 包围的子文件夹的文件夹中尝试了它。修复很简单,@($_ | Get-ChildItem -Recurse | Where { !$_.PSIsContainer })(而不是Get-ChildItem $_.FullName -Recurse)。
    • 这条管道的一个负面因素是它正在收集完整的集合,然后对其进行操作,据我所知.. 所以在一棵大树中,它会扫描你之前的所有内容,因此会有很长的延迟看到任何被删除的东西。这里有一个脚本:powershelladmin.com/wiki/Script_to_delete_empty_folders,它要复杂得多,但可以提供更直接的反馈。
    【解决方案2】:

    本着第一个答案的精神,这是删除空目录的最短方法:

    ls -recurse | where {!@(ls -force $_.fullname)} | rm -whatif
    

    当目录有隐藏文件夹时需要 -force 标志,例如 .svn

    【讨论】:

    • 它不会删除嵌套的空目录,你有解决方法吗?
    • 他的意思是在一个目录结构md a\b\c中c为空,b也没有其他文件,这样只会删除c。他需要整个空的 \b\c 子树,就像 Keith 的脚本一样。
    【解决方案3】:

    这将在解决空嵌套目录问题的父目录之前对子目录进行排序。

    dir -Directory -Recurse |
        %{ $_.FullName} |
        sort -Descending |
        where { !@(ls -force $_) } |
        rm -WhatIf
    

    【讨论】:

      【解决方案4】:

      添加到最后一个:

      while (Get-ChildItem $StartingPoint -recurse | where {!@(Get-ChildItem -force $_.fullname)} | Test-Path) {
          Get-ChildItem $StartingPoint -recurse | where {!@(Get-ChildItem -force $_.fullname)} | Remove-Item
      }
      

      这将使它完成,它将继续搜索以删除 $StartingPoint 下的任何空文件夹

      【讨论】:

      • 你指的是哪个答案?对答案的相对位置的引用并不可靠,因为它们取决于视图(投票/最新/活动)和接受答案的变化以及随时间变化(投票、活动和接受状态)。
      【解决方案5】:

      我需要一些对企业友好的功能。这是我的看法。

      我从其他答案的代码开始,然后添加了一个带有原始文件夹列表的 JSON 文件(包括每个文件夹的文件数)。删除空目录并记录它们。

      https://gist.github.com/yzorg/e92c5eb60e97b1d6381b

      param (
          [switch]$Clear
      )
      
      # if you want to reload a previous file list
      #$stat = ConvertFrom-Json (gc dir-cleanup-filecount-by-directory.json -join "`n")
      
      if ($Clear) { 
          $stat = @() 
      } elseif ($stat.Count -ne 0 -and (-not "$($stat[0].DirPath)".StartsWith($PWD.ProviderPath))) {
          Write-Warning "Path changed, clearing cached file list."
          Read-Host -Prompt 'Press -Enter-'
          $stat = @() 
      }
      
      $lineCount = 0
      if ($stat.Count -eq 0) {
          $stat = gci -Recurse -Directory | %{  # -Exclude 'Visual Studio 2013' # test in 'Documents' folder
      
              if (++$lineCount % 100 -eq 0) { Write-Warning "file count $lineCount" }
      
              New-Object psobject -Property @{ 
                  DirPath=$_.FullName; 
                  DirPathLength=$_.FullName.Length;
                  FileCount=($_ | gci -Force -File).Count; 
                  DirCount=($_ | gci -Force -Directory).Count
              }
          }
          $stat | ConvertTo-Json | Out-File dir-cleanup-filecount-by-directory.json -Verbose
      }
      
      $delelteListTxt = 'dir-cleanup-emptydirs-{0}-{1}.txt' -f ((date -f s) -replace '[-:]','' -replace 'T','_'),$env:USERNAME
      
      $stat | 
          ? FileCount -eq 0 | 
          sort -property @{Expression="DirPathLength";Descending=$true}, @{Expression="DirPath";Descending=$false} |
          select -ExpandProperty DirPath | #-First 10 | 
          ?{ @(gci $_ -Force).Count -eq 0 } | %{
              Remove-Item $_ -Verbose # -WhatIf  # uncomment to see the first pass of folders to be cleaned**
              $_ | Out-File -Append -Encoding utf8 $delelteListTxt
              sleep 0.1
          }
      
      # ** - The list you'll see from -WhatIf isn't a complete list because parent folders
      #      might also qualify after the first level is cleaned.  The -WhatIf list will 
      #      show correct breath, which is what I want to see before running the command.
      

      【讨论】:

        【解决方案6】:

        要删除超过 30 天的文件:

        get-childitem -recurse |
            ? {$_.GetType() -match "FileInfo"} |
            ?{ $_.LastWriteTime -lt [datetime]::now.adddays(-30) }  |
            rm -whatif
        

        (只需删除-whatif 即可实际执行。)

        跟进:

         get-childitem -recurse |
             ? {$_.GetType() -match "DirectoryInfo"} |
             ?{ $_.GetFiles().Count -eq 0 -and $_.GetDirectories().Count -eq 0 } |
             rm -whatif
        

        【讨论】:

        • 如果要匹配类型,可以使用 -is 运算符,例如$_ -is [IO.FileInfo]。
        • 那些尾随的反引号是多余的。没有它们,代码也能正常工作。
        【解决方案7】:

        这对我有用。

        $limit = (Get-Date).AddDays(-15) 
        
        $path = "C:\Some\Path"
        

        删除早于$limit的文件:

        Get-ChildItem -Path $path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | Remove-Item -Force
        

        删除旧文件后留下的所有空目录:

        Get-ChildItem -Path $path -Recurse -Force | Where-Object { $_.PSIsContainer -and (Get-ChildItem -Path $_.FullName -Recurse -Force | Where-Object { !$_.PSIsContainer }) -eq $null } | Remove-Item -Force -Recurse
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-07-05
          • 2017-10-30
          • 2010-12-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多