如何使用 PowerShell 删除空子文件夹?

Posted

技术标签:

【中文标题】如何使用 PowerShell 删除空子文件夹?【英文标题】:How to delete empty subfolders with 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 的情况下清理子文件夹? (“垃圾”文件夹将始终保留。)

【问题讨论】:

【参考方案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

【讨论】:

以上是关于如何使用 PowerShell 删除空子文件夹?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 PowerShell 删除 OneDrive 中的非空文件夹?

如何使用 PowerShell 递归删除具有特定名称的文件夹?

如何递归删除PowerShell中的所有空文件夹?

需要 Windows 批处理命令单线器按名称删除文件夹,而不是按 date.time 使用 powershell(如果适用)

如何使用批处理或PowerShell从文本文件中删除换行符

如何在 PowerShell 中悄悄删除包含内容的目录