如何使用 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
想要的结果:
删除:FILE3b
、subfolder2B
和 subfolder3a
。
离开:subfolder1a
、subfolder2a
和 FILE3a
。
我可以递归地清理文件。如何在不删除 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 新手 - '@' 是什么/它是如何工作的? @([]
包围的子文件夹的文件夹中尝试了它。修复很简单,@($_ | 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 递归删除具有特定名称的文件夹?
需要 Windows 批处理命令单线器按名称删除文件夹,而不是按 date.time 使用 powershell(如果适用)