How to recursively remove all empty folders in PowerShell?

前端 未结 13 2686
野趣味
野趣味 2020-12-08 02:23

I need to recursively remove all empty folders for a specific folder in PowerShell (checking folder and sub-folder at any level).

At the moment I am using this scrip

相关标签:
13条回答
  • 2020-12-08 02:38
    Get-ChildItem $tdc -Recurse -Force -Directory | 
        Sort-Object -Property FullName -Descending |
        Where-Object { $($_ | Get-ChildItem -Force | Select-Object -First 1).Count -eq 0 } |
        Remove-Item -Verbose
    

    The only novel contribution here is using Sort-Object to reverse sort by the directory's FullName. This will ensure that we always process children before we process parents (i.e., "tail recursion" as described by Kirk Munro's answer). That makes it recursively remove empty folders.

    Off hand, I'm not sure if the Select-Object -First 1 will meaningfully improve performance or not, but it may.

    0 讨论(0)
  • 2020-12-08 02:39

    Recursively removing empty subdirectories can also be accomplished using a "For Loop".

    Before we start, let's make some subdirectories & text files to work with in $HOME\Desktop\Test

    MD $HOME\Desktop\Test\0\1\2\3\4\5 
    MD $HOME\Desktop\Test\A\B\C\D\E\F
    MD $HOME\Desktop\Test\A\B\C\DD\EE\FF
    MD $HOME\Desktop\Test\Q\W\E\R\T\Y
    MD $HOME\Desktop\Test\Q\W\E\RR
    "Hello World" > $HOME\Desktop\Test\0\1\Text1.txt
    "Hello World" > $HOME\Desktop\Test\A\B\C\D\E\Text2.txt
    "Hello World" > $HOME\Desktop\Test\A\B\C\DD\Text3.txt
    "Hello World" > $HOME\Desktop\Test\Q\W\E\RR\Text4.txt
    

    First, store the following Script Block in the variable $SB. The variable can be called later using the &SB command. The &SB command will output a list of empty subdirectories contained in $HOME\Desktop\Test

    $SB = {
        Get-ChildItem $HOME\Desktop\Test -Directory -Recurse |
        Where-Object {(Get-ChildItem $_.FullName -Force).Count -eq 0}
    }
    

    NOTE: The -Force parameter is very important. It makes sure that directories which contain hidden files and subdirectories, but are otherwise empty, are not deleted in the "For Loop".

    Now use a "For Loop" to recursively remove empty subdirectories in $HOME\Desktop\Test

    For ($Empty = &$SB ; $Empty -ne $null ; $Empty = &$SB) {Remove-Item (&$SB).FullName}
    

    Tested as working on PowerShell 4.0

    0 讨论(0)
  • 2020-12-08 02:40

    Just figured I would contribute to the already long list of answers here.

    Many of the answers have quirks to them, like needing to run more than once. Others are overly complex for the average user (like using tail recursion to prevent duplicate scans, etc).

    Here is a very simple one-liner that I've been using for years, and works great...

    It does not account for hidden files/folders, but you can fix that by adding -Force to the Get-ChildItem command

    This is the long, fully qualified cmdlet name version:

    Get-ChildItem -Recurse -Directory | ? {-Not $_.EnumerateFiles('*',1) | Select-Object -First 1} | Remove-Item -Recurse
    

    So basically...here's how it goes:

    • Get-ChildItem -Recurse -Directory - Start scanning recursively looking for directories
    • $_.EnumerateFiles('*',1) - For each directory...Enumerate the files
      • EnumerateFiles will output its findings as it goes, GetFiles will output when it is done....at least, that's how it is supposed to work in .NET...for some reason in PowerShell GetFiles starts spitting out immediately. But I still use EnumerateFiles because in testing it was reliably faster.
      • ('*',1) means find ALL files recursively.
    • | Select-Object -First 1 - Stop at the first file found
      • This was difficult to test how much it helped. In some cases it helped tremendously, other times it didn't help at all, and in some cases it slowed it down by a small amount. So I really don't know. I guess this is optional.
    • | Remove-Item -Recurse - Remove the directory, recursively (ensures directories that contain empty sub directories gets removed)

    If you're counting characters, this could be shortened to:

    ls -s -ad | ? {-Not $_.EnumerateFiles('*',1) | select -First 1} | rm -Recurse
    
    • -s - alias for -Recurse
    • -ad - alias for -Directory

    If you really don't care about performance because you don't have that many files....even more so to:

    ls -s -ad | ? {!($_.GetFiles('*',1))} | rm -Recurse
    

    Side note: While playing around with this, I started testing various versions with Measure-Command against a server with millions of files and thousands of directories.

    This is faster than the command I've been using (above):

    (gi .).EnumerateDirectories('*',1) | ? {-Not $_.EnumerateFiles('*',1) } | rm -Recurse
    
    0 讨论(0)
  • 2020-12-08 02:41
    ls c:\temp -rec |%{ if ($_.PSIsContainer -eq $True) {if ( (ls $_.fullname -rec | measure |select -expand count ) -eq "0"  ){ ri $_.fullname -whatif}  }  }  
    
    0 讨论(0)
  • 2020-12-08 02:52

    You can use this:

    $tdc="C:\a\c\d"
    $dirs = gci $tdc -directory -recurse | Where { (gci $_.fullName).count -eq 0 } | select -expandproperty FullName
    $dirs | Foreach-Object { Remove-Item $_ }
    

    $dirs will be an array of empty directories returned from the Get-ChildItem command after filtering. You can then loop over it to remove the items.

    Update

    If you want to remove directories that contain empty directories, you just need to keep running the script until they're all gone. You can loop until $dirs is empty:

    $tdc="C:\a\c\d"
    do {
      $dirs = gci $tdc -directory -recurse | Where { (gci $_.fullName).count -eq 0 } | select -expandproperty FullName
      $dirs | Foreach-Object { Remove-Item $_ }
    } while ($dirs.count -gt 0)
    

    If you want to ensure that hidden files and folders will also be removed, include the -Force flag:

    do {
      $dirs = gci $tdc -directory -recurse | Where { (gci $_.fullName -Force).count -eq 0 } | select -expandproperty FullName
      $dirs | Foreach-Object { Remove-Item $_ }
    } while ($dirs.count -gt 0)
    
    0 讨论(0)
  • 2020-12-08 02:54

    Assuming you're inside the parent folder of interest

    gci . -Recurse -Directory | % { if(!(gci -Path $_.FullName)) {ri -Force -Recurse $_.FullName} }

    For your case with $tdc it'll be

    gci $tdc -Recurse -Directory | % { if(!(gci -Path $_.FullName)) {ri -Force -Recurse $_.FullName} }

    0 讨论(0)
提交回复
热议问题