PowerShell:查找并删除文件夹或子文件夹中没有文件的文件夹

我有一个PowerShell 2.0脚本,用于删除文件夹中没有文件:

dir 'P:\path\to\wherever' -recurse | Where-Object { $_.PSIsContainer } | Where-Object { $_.GetFiles().Count -eq 0 } | foreach-object { remove-item $_.fullname -recurse} 

但是,我注意到运行脚本时出现了很多错误。 即:

 Remove-Item : Directory P:\path\to\wherever cannot be removed because it is not empty. 

“什么?!” 我惊慌失措 他们应该都是空的! 我只筛选空文件夹! 显然,这不是脚本如何工作。 在这种情况下只有文件夹作为子文件夹,但作为孙子文件被视为空的文件:

 Folder1 (no files - 1 folder) \ Folder 2 (one file) 

在这种情况下,PowerShell将Folder1看作是空的,并尝试删除它。 这使我感到困惑的原因是,如果我在Windows资源pipe理器中右键单击Folder1它说Folder1有1个文件夹和1个文件。 无论用于计算Folder1下面的子对象允许它看到孙子对象都无限。

题:

如何让我的脚本不考虑一个文件夹为空,如果它有孙子或以外的文件?

更新递归删除:

你可以使用如下所示的嵌套管道:

 dir -recurse | Where {$_.PSIsContainer -and ` @(dir -Lit $_.Fullname -r | Where {!$_.PSIsContainer}).Length -eq 0} | Remove-Item -recurse -whatif 

(从这里 – 如何删除与PowerShell空子文件夹? )


添加一个($_.GetDirectories().Count -eq 0)条件:

 dir path -recurse | Where-Object { $_.PSIsContainer } | Where-Object { ($_.GetFiles().Count -eq 0) -and ($_.GetDirectories().Count -eq 0) } | Remove-Item 

这是一个更简洁的方式做到这一点,虽然:

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

请注意,在删除项目时,您不需要Foreach-Object 。 还要添加一个-whatifRemove-Item ,看看它是否会做你所期望的。

这是我在最近的脚本中使用的递归函数…

 function DeleteEmptyDirectories { param([string] $root) [System.IO.Directory]::GetDirectories("$root") | % { DeleteEmptyDirectories "$_"; if ([System.IO.Directory]::GetFileSystemEntries("$_").Length -eq 0) { Write-Output "Removing $_"; Remove-Item -Force "$_"; } }; } DeleteEmptyDirectories "P:\Path\to\wherever"; 

在制作这个脚本时有一些问题,其中一个是用来检查一个文件夹是否为空:

 {!$_.PSIsContainer}).Length -eq 0 

不过,我发现空文件夹的大小不是0,而是NULL。 以下是我将使用的PowerShell脚本。 这不是我自己的。 相反,它来自PowerShell MVP Richard Siddaway 。 您可以在PowerShell.com的此线程上看到此函数来自的线程 。

 function remove-emptyfolder { param ($folder) foreach ($subfolder in $folder.SubFolders){ $notempty = $false if (($subfolder.Files | Measure-Object).Count -gt 0){$notempty = $true} if (($subFolders.SubFolders | Measure-Object).Count -gt 0){$notempty = $true} if ($subfolder.Size -eq 0 -and !$notempty){ Remove-Item -Path $($subfolder.Path) -Force -WhatIf } else { remove-emptyfolder $subfolder } } } $path = "c:\test" $fso = New-Object -ComObject "Scripting.FileSystemObject" $folder = $fso.GetFolder($path) remove-emptyfolder $folder 

你可以使用递归函数。 其实我已经写了一个:

 cls $dir = "C:\MyFolder" Function RecurseDelete() { param ( [string]$MyDir ) IF (!(Get-ChildItem -Recurse $mydir | Where-Object {$_.length -ne $null})) { Write-Host "Deleting $mydir" Remove-Item -Recurse $mydir } ELSEIF (Get-ChildItem $mydir | Where-Object {$_.length -eq $null}) { ForEach ($sub in (Get-ChildItem $mydir | Where-Object {$_.length -eq $null})) { Write-Host "Checking $($sub.fullname)" RecurseDelete $sub.fullname } } ELSE { IF (!(Get-ChildItem $mydir)) { Write-Host "Deleting $mydir" Remove-Item $mydir } } } IF (Test-Path $dir) {RecurseDelete $dir}