Jump to page sections
- Recursively Remove Empty Folders, using Tail Recursion in PowerShell
- Function Code
- Test Run
- Alternative
Recursively Remove Empty Folders, using Tail Recursion in PowerShell
Some time around 2011-2014, I wrote a script to recursively delete empty folders (if you visited the old link from some forum or blog, you have now been redirected to this updated article). I feel obligated to provide the visitors with a better alternative since the old, dead link still gets some poor visitors.
My solution is loosely based on Kirk Munroe's excellent answer on Stack Overflow. He explains that Get-ChildItem performs head recursion, while what we need is tail recursion.
Quote: "Since you want to remove empty folders, and also remove their parent if they are empty after you remove the empty folders, you need to use tail recursion instead, which processes the folders from the deepest child up to the root. By using tail recursion, there will be no need for repeated calls to the code that removes the empty folders -- one call will do it all for you."
It also handles hidden files and directories. A directory containing a hidden file will not be deleted, while an empty hidden directory will be deleted.
This code is compatible with PowerShell version 2, and there are also a few minor tweaks (PSv2 compatibility = not using the -Directory parameter for Get-ChildItem).
Function Code
Commented and mostly self-explanatory. For syntax highlighting or downloading a script file, visit GitHub here.
function Remove-EmptyFolders { <# .SYNOPSIS Remove empty folders recursively from a root directory. The root directory itself is not removed. Author: Joakim Borger Svendsen, Svendsen Tech, Copyright 2022. MIT License. .EXAMPLE . .\Remove-EmptyFolders.ps1 Remove-EmptyFolders -Path E:\FileShareFolder .EXAMPLE Remove-EmptyFolders -Path \\server\share\data NB. You might have to change $ChildDirectory.FullName to $ChildDirectory.ProviderPath in the code for this to work. Untested with UNC paths as of 2022-01-28. #> [CmdletBinding()] Param( [String] $Path ) Begin { [Int32] $Script:Counter = 0 if (++$Counter -eq 1) { $RootPath = $Path Write-Verbose -Message "Saved root path as '$RootPath'." } # Avoid overflow. Overly cautious? ~2.15 million directories... if ($Counter -eq [Int32]::MaxValue) { $Counter = 1 } } Process { # List directories. foreach ($ChildDirectory in Get-ChildItem -LiteralPath $Path -Force | Where-Object {$_.PSIsContainer}) { # Use .ProviderPath on Windows instead of .FullName, # in order to support UNC paths (untested). # Process each child directory recursively. Remove-EmptyFolders -Path $ChildDirectory.FullName } $CurrentChildren = Get-ChildItem -LiteralPath $Path -Force # If it's empty, the condition below evaluates to true. Get-ChildItem # returns $null for empty folders. if ($null -eq $CurrentChildren) { # Do not delete the root folder itself. if ($Path -ne $RootPath) { Write-Verbose -Message "Removing empty folder '$Path'." Remove-Item -LiteralPath $Path -Force } } } }
Test Run
We see that it removes all the folders except ./testdeldir/a/d, which contains a file. The recursion works, and nested empty directories are deleted up to the root path (but not including the root path).
PS /tmp> New-Item -ItemType Directory -Path ./testdeldir/a/x/g/d | Out-Null PS /tmp> New-Item -ItemType Directory -Path ./testdeldir/a/b/c/e | Out-Null PS /tmp> New-Item -ItemType Directory -Path ./testdeldir/a/c | Out-Null PS /tmp> New-Item -ItemType Directory -Path ./testdeldir/a/d/f | Out-Null PS /tmp> 'testfile' | Set-Content ./testdeldir/a/d/file.txt PS /tmp> . ./Remove-EmptyFolders.ps1 # dot-source to load the function PS /tmp> Remove-EmptyFolders -Path ./testdeldir/ -Verbose VERBOSE: Saved root path as './testdeldir/'. VERBOSE: Removing empty folder '/tmp/testdeldir/a/b/c/e'. VERBOSE: Removing empty folder '/tmp/testdeldir/a/b/c'. VERBOSE: Removing empty folder '/tmp/testdeldir/a/b'. VERBOSE: Removing empty folder '/tmp/testdeldir/a/c'. VERBOSE: Removing empty folder '/tmp/testdeldir/a/d/f'. VERBOSE: Removing empty folder '/tmp/testdeldir/a/x/g/d'. VERBOSE: Removing empty folder '/tmp/testdeldir/a/x/g'. VERBOSE: Removing empty folder '/tmp/testdeldir/a/x'. PS /tmp> gci -rec ./testdeldir/ Directory: /tmp/testdeldir UnixMode User Group LastWriteTime Size Name -------- ---- ----- ------------- ---- ---- drwxrwxr-x joakim joakim 1/28/2022 23:14 4096 a Directory: /tmp/testdeldir/a UnixMode User Group LastWriteTime Size Name -------- ---- ----- ------------- ---- ---- drwxrwxr-x joakim joakim 1/28/2022 23:14 4096 d Directory: /tmp/testdeldir/a/d UnixMode User Group LastWriteTime Size Name -------- ---- ----- ------------- ---- ---- -rw-rw-r-- joakim joakim 1/28/2022 23:14 9 file.txt
Alternative
As mentioned on Stack Overflow, in this ingenius approach, the simple piece of code below also works. It sorts descending, ensuring children are always processed before parents. This makes it delete recursively when it tests the, depth-first, directories for children and deleted if the count of items inside is 0. Hidden files and folders are handled, by using the -Force parameter for Get-ChildItem.
Get-ChildItem -LiteralPath $FolderToProcess -Recurse -Force -Directory | Sort-Object -Property FullName -Descending | Where-Object { ($_ | Get-ChildItem -Force | Select-Object -First 1).Count -eq 0 } | Remove-Item -Verbose
To make this compatible with PowerShell version 2, you can use this:
Get-ChildItem -LiteralPath $FolderToProcess -Recurse -Force | Where-Object {$_.PSIsContainer} Sort-Object -Property FullName -Descending | Where-Object {-not ($_ | Get-ChildItem -Force | Select-Object -First 1)} | Remove-Item -VerbosePowershell Windows Recursion Function Programming
Blog articles in alphabetical order
A
- A Look at the KLP AksjeNorden Index Mutual Fund
- A primitive hex version of the seq gnu utility, written in perl
- Accessing the Bing Search API v5 using PowerShell
- Accessing the Google Custom Search API using PowerShell
- Active directory password expiration notification
- Aksje-, fonds- og ETF-utbytterapportgenerator for Nordnet-transaksjonslogg
- Ascii art characters powershell script
- Automatically delete old IIS logs with PowerShell
C
- Calculate and enumerate subnets with PSipcalc
- Calculate the trend for financial products based on close rates
- Check for open TCP ports using PowerShell
- Check if an AD user exists with Get-ADUser
- Check when servers were last patched with Windows Update via COM or WSUS
- Compiling or packaging an executable from perl code on windows
- Convert between Windows and Unix epoch with Python and Perl
- Convert file encoding using linux and iconv
- Convert from most encodings to utf8 with powershell
- ConvertTo-Json for PowerShell version 2
- Create cryptographically secure and pseudorandom data with PowerShell
- Crypto is here - and it is not going away
- Crypto logo analysis ftw
D
G
- Get rid of Psychology in the Stock Markets
- Get Folder Size with PowerShell, Blazingly Fast
- Get Linux disk space report in PowerShell
- Get-Weather cmdlet for PowerShell, using the OpenWeatherMap API
- Get-wmiobject wrapper
- Getting computer information using powershell
- Getting computer models in a domain using Powershell
- Getting computer names from AD using Powershell
- Getting usernames from active directory with powershell
- Gnu seq on steroids with hex support and descending ranges
- Gullpriser hos Gullbanken mot spotprisen til gull
H
- Have PowerShell trigger an action when CPU or memory usage reaches certain values
- Historical view of the SnP 500 Index since 1927, when corona is rampant in mid-March 2020
- How to check perl module version
- How to list all AD computer object properties
- Hva det innebærer at særkravet for lån til sekundærbolig bortfaller
I
L
M
P
- Parse openssl certificate date output into .NET DateTime objects
- Parse PsLoggedOn.exe Output with PowerShell
- Parse schtasks.exe Output with PowerShell
- Perl on windows
- Port scan subnets with PSnmap for PowerShell
- PowerShell Relative Strength Index (RSI) Calculator
- PowerShell .NET regex to validate IPv6 address (RFC-compliant)
- PowerShell benchmarking module built around Measure-Command
- Powershell change the wmi timeout value
- PowerShell check if file exists
- Powershell check if folder exists
- PowerShell Cmdlet for Splitting an Array
- PowerShell Executables File System Locations
- PowerShell foreach loops and ForEach-Object
- PowerShell Get-MountPointData Cmdlet
- PowerShell Java Auto-Update Script
- Powershell multi-line comments
- Powershell prompt for password convert securestring to plain text
- Powershell psexec wrapper
- PowerShell regex to accurately match IPv4 address (0-255 only)
- Powershell regular expressions
- Powershell split operator
- Powershell vs perl at text processing
- PS2CMD - embed PowerShell code in a batch file
R
- Recursively Remove Empty Folders, using PowerShell
- Remote control mom via PowerShell and TeamViewer
- Remove empty elements from an array in PowerShell
- Remove first or last n characters from a string in PowerShell
- Rename unix utility - windows port
- Renaming files using PowerShell
- Running perl one-liners and scripts from powershell
S
- Sammenlign gullpriser og sølvpriser hos norske forhandlere av edelmetall
- Self-contained batch file with perl code
- Simple Morningstar Fund Report Script
- Sort a list of computers by domain first and then name, using PowerShell
- Sort strings with numbers more humanely in PowerShell
- Sorting in ascending and descending order simultaneously in PowerShell
- Spar en slant med en optimalisert kredittkortportefølje
- Spre finansiell risiko på en skattesmart måte med flere Aksjesparekontoer
- SSH from PowerShell using the SSH.NET library
- SSH-Sessions Add-on with SCP SFTP Support
- Static Mutual Fund Portfolio the Last 2 Years Up 43 Percent
- STOXR - Currency Conversion Software - Open Exchange Rates API