简体   繁体   中英

PowerShell: Find and delete folders that have no files in them or in child folders

I have a PowerShell 2.0 script that I use to delete folders that have no files in them:

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

However, I noticed that there were a ton of errors when running the script. Namely:

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

"WHAT?!" I panicked. They should all be empty. I filter for only empty folders, Apparently that's not quite how the script is working: In this scenario a folder that has only folders as children, but files as grandchildren is considered empty of files:

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

In that case, PowerShell sees Folder1 as being empty and tries to delete it. The reason this puzzles me is because if I right-click on Folder1 in Windows Explorer It says that Folder1 has 1 folder and 1 file within it. Whatever is used to calculate the child objects underneath Folder1 from within Explorer allows it to see grandchild objects ad infinitum.

Question:

How can I make my script not consider a folder empty if it has files as grandchildren or beyond?

Updating for recursive deletion:

You can use a nested pipeline like below:

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

(from here - How to delete empty subfolders with PowerShell? )


Add a ($_.GetDirectories().Count -eq 0) condition too:

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

Here is a more succinct way of doing this though:

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

Note that you do not need the Foreach-Object while doing remove item. Also add a -whatif to the Remove-Item to see if it is going to do what you expect it to.

Here's a recursive function I used in a recent script...

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";

There were some issues in making this script, one of them being using this to check if a folder is empty:

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

However, I discovered that empty folders are not sized with 0 but rather NULL. The following is the PowerShell script that I will be using. It is not my own. Rather, it is from PowerShell MVP Richard Siddaway . You can see the thread that this function comes from over at this thread on 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

You can use a recursive function for this. I actually have already written one:

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}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM