Delete Folders where count>5 - powershell

I Need some help here: the following script needs to be changed so, that the script delete only Folders in the Subdirectory not Files. Can anyone help me?
$path = "C:\test\1"
$keep = 3
$strLogFileName = "c:\test\yourlogfile.log";
function Log-Message
{
Param ([string]$logtext)
Add-content $strLogFileName -value $logtext
}
$dirs = Get-ChildItem -Path $path -Recurse | Where-Object {$_.PsIsContainer}
foreach ($dir in $dirs) {
$files = Get-ChildItem -Path $dir.FullName | Where-Object {-not $_.PsIsContainer -and $_.name - like "*.zip"}
if ($files.Count -gt $keep) {
$files | Sort-Object CreationTime -desc| Select-Object -First ($files.Count - $keep) |
% { $dt=get-date;(Log-Message "Deleting File $_ on $dt");$_ }| Remove-Item -Force
}
}

My original answer was -WAY- off base, and having RTFQ I've got something that should work for you.
function Remove-LargeFolders
{
Param([string]$RootPath)
$keep = 5
#Get a list of the dirs in the first level of the folder
$dirs = Get-ChildItem -Path $RootPath | Where-Object {$_.PsIsContainer}
foreach ($dir in $dirs) {
#Call function on the new folder to check all the sublevels before deleting
#the top-level folder.
Remove-LargeFolders $dir.FullName
$files = Get-ChildItem -Path $dir.FullName | Where-Object {-not $_.PsIsContainer `
-and $_.name -like "*.zip"}
if ($files.Count -gt $keep) {
$dt=get-date
Log-Message "Deleting Folder $dir on $dt"
$dir
Remove-Item $dir.FullName -Force -Recurse
}
}
}
This should do what you're looking for. The answer was recursion! Essentially, the script only looks at one level of folders at a time. It digs down to the bottom of each folder tree, before working it's way back up to the top.
This script will work as written, but there is a proviso. Right now, if the script is targeting a structure like this:
Target Folder (4 files)
SubFolder (10 files)
SubSubFolder (5 files)
It will check SubSubFolder first and not delete it, since it has 5 or fewer files. However, once it hops back up to SubFolder, it will see a folder that is too big and kill it off, getting rid of SubSubFolder in the process. If you want a way around that, you'll need to build in some checks that will allow you to see if each folder has another folder in it before deleting.
Hope this helps more!

Related

Filter and delete files and folders(and files inside of folders) older than x days in powershell

this is my first post on this forum. Im a beginner in coding and I need help with one of my very first self coded tools.
I made a small script, which deletes files based on if they are older than date x (lastwritetime). Now to my problem: I want the script also to check for files inside of folders inside of a directory and only delete a folder afterwards if it is truly empty. I cant figure out how to solve the recursion in this problem, seems like the script deletes just the entire folder in relation to the date x. Could anyone tell me please what I missed in this code and help me to create a own recursion to solve the problem or fix the code? Thanks to you all, guys! Here is my code:
I would be glad if someone knows how to make the code work by using a function
$path = Read-Host "please enter your path"
"
"
$timedel = Read-Host "Enter days in the past (e.g -12)"
$dateedit = (Get-Date).AddDays($timedel)
"
"
Get-ChildItem $path -File -Recurse | foreach{ if ($_.LastWriteTime -and !$_.LastAccessTimeUtc -le $dateedit) {
Write-Output "older as $timedel days: ($_)" } }
"
"
pause
Get-ChildItem -Path $path -Force -Recurse | Where-Object { $_.PsisContainer -and $_.LastWriteTime -le $dateedit } | Remove-Item -Force -Recurse
""
Write-Output "Files deleted"
param(
[IO.DirectoryInfo]$targetTolder = "d:\tmp",
[DateTime]$dateTimeX = "2020-11-15 00:00:00"
)
Get-ChildItem $targetTolder -Directory -Recurse | Sort-Object {$_.FullName} -Descending | ForEach-Object {
Get-ChildItem $_ -File | Where-Object {$_.LastWriteTime -lt $dateTimeX} | Remove-Item -Force
if ((Get-ChildItem $_).Count -eq 0){Remove-Item $_ -Force}
}
remove -WhatIf after test
To also remove folders that are older than the set days in the past if they are empty leaves you with the problem that as soon as a file is removed from such a folder, the LastWriteTime of the folder is set to that moment in time.
This means you should get a list of older folders first, before you start deleting older files and use that list afterwards to also remove these folders if they are empty.
Also, a minimal check on user input from Read-Host should be done. (i.e. the path must exist and the number of days must be convertable to an integer number. For the latter I chose to simply cast it to [int] because if that fails, the code would generate an execption anyway.
Try something like
$path = Read-Host "please enter your path"
# test the user input
if (-not (Test-Path -Path $path -PathType Container)) {
Write-Error "The path $path does not exist!"
}
else {
$timedel = Read-Host "Enter days in the past (e.g -12)"
# convert to int and make sure it is a negative value
$timedel = -[Math]::Abs([int]$timedel)
$dateedit = (Get-Date).AddDays($timedel).Date # .Date sets this date to midnight (00:00:00)
# get a list of all folders (FullNames only)that have a LastWriteTime older than the set date.
# we check this list later to see if any of the folders are empty and if so, delete them.
$folders = (Get-ChildItem -Path $path -Directory -Recurse | Where-Object { $_.LastWriteTime -le $dateedit }).FullName
# get a list of files to remove
Get-ChildItem -Path $path -File -Recurse | Where-Object { $_.LastWriteTime -le $dateedit} | ForEach-Object {
Write-Host "older as $timedel days: $($_.FullName)"
$_ | Remove-Item -Force -WhatIf # see below about the -WhatIf safety switch
}
# now that old files are gone, test the folder list we got earlier and remove any if empty
$folders | ForEach-Object {
if ((Get-ChildItem -Path $_ -Force).Count -eq 0) {
Write-Host "Deleting empty folder: $_"
$_ | Remove-Item -Force -WhatIf # see below about the -WhatIf safety switch
}
}
Write-Host "All Done!" -ForegroundColor Green
}
The -WhatIf switch used on Remove-Item is there for your own safety. With that, no file or folder is actually deleted, instead in the console it is written what would be deleted. If you are satisfied that this is all good, remove the -WhatIf and run the code again to really delete the files and folders
try something like this:
$timedel=-12
#remove old files
Get-ChildItem "C:\temp" -Recurse -File | Where LastWriteTime -lt (Get-Date).AddDays($timedel) | Remove-Item -Force
#remove directory without file
Get-ChildItem "C:\temp\" -Recurse -Directory | where {(Get-ChildItem $_.FullName -Recurse -File).count -eq 0} | Remove-Item -Force -recurse

Why exclude clause not working as expected.. powershell?

I have a powershell script designed to go through some backup files, archive the month end backup(if its the first of the month) and delete anything older than 9 days after that. I'm trying to tell it to exclude the archive folder but it seems to be ignoring that and I'm unsure why.. kind of a noob to powershell.
I've tried using -notlike $Exlcude+% I've tried -notmatch -NE .. I even tried notcontains even though I knew that wouldn't work.. I tried applying the $exclude to every portion of the code where that folder might be accessed and its still making a copy of it.
#If first of the month copy latest backups to Monthly Backup Folder - will copy full folder path
PARAM($BackupPath="\\SomeServer\sqltestbackups\",
$Exclude= "\\SomeServer\sqltestbackups\Archive")
#Grab a recursive list of all subfolders
$SubFolders = dir $BackupPath -Recurse | Where-Object {$_.PSIsContainer} | ForEach-Object -Process {$_.FullName}
#Iterate through the list of subfolders and grab the first file in each
$Year = (get-date).Year
$Month = (get-date).Month
$StartOfMonth = Get-Date -Year $Year -Month $Month -Day 1
$Today = (Get-Date).day
$Date = (GET-DATE).AddDays(-9)
$PrevMonth = (GET-DATE).year.ToString()+(Get-Culture).DateTimeFormat.GetMonthName((Get-Date).AddMonths(-1).month)
$Destination=$Exclude + "\" + $Prevmonth +"\"
IF (!(Test-Path -path $destination)) {New-Item $destination -Type Directory}
IF($Today -eq '5')
{
$Path = $BackupPath #Root path to look for files
$DestinationPath = $Destination #Remote destination for file copy
#Grab a recursive list of all subfolders
$SubFolders = dir $Path -Recurse | Where-Object {$_.PSIsContainer -and $_.DirectoryName -notmatch $Exclude} | ForEach-Object -Process {$_.FullName}
#Iterate through the list of subfolders and grab the first file in each
ForEach ($Folder in $SubFolders)
{
$FullFileName = dir $Folder | Where-Object {!$_.PSIsContainer -and $_.DirectoryName -notmatch $Exclude} | Sort-Object {$_.LastWriteTime} -Descending | Select-Object -First 1
#For every file grab it's location and output the robocopy command ready for use
ForEach ($File in $FullFileName)
{
$FilePath = $File.DirectoryName
$ArchFolder = $File.DirectoryName
$ArchFolder=$ArchFolder.Replace($BackupPath,"")+"\"
$FinalPath=$destinationPath+$ArchFolder
$FileName = $File.Name
robocopy $FilePath $FinalPath $FileName /A-:SH /R:6 /W:30 /Z
}
}
}
# Delete files older than 9 days that are not contained within the month end folder
Get-ChildItem $BackupPath -Recurse |
Where-Object { !($_.PSIsContainer) -and
$_.LastWriteTime -lt $Date -and
$_.Directory -notlike $Exclude+"%" } |
Remove-Item
The code works except for the copy month end portion.. in this portion it is including the archive folder and I end up with it copying the previous month.. the script is designed to put the files in archive/YM/FullPath of backup so what is happening is its going Archive/YM/ARchive/YM/FullPath even though I'm trying SO HARD to exclude this path from the folders.
Example of whats going wrong with the robocopy
Source : \SomeServer\sqltestbackups\ARCHIVE\2019March\SomeOtherServer\SQLBackups\SomeDatabase\master\
Dest : \SomeServer\sqltestbackups\Archive\2019March\ARCHIVE\2019March\SomeOtherServer\SQLBackups\SomeDatabase\master\
the type DirectoryInfodoes not have a property DirectoryName. Try property BaseName instead.
# dir, gci are aliases for Get-ChildItem
Get-ChildItem $Path -Recurse `
| Where-Object { $_.PSIsContainer -and $_.BaseName -notmatch $Exclude }
works.
To see which types are returned and which members there are type
Get-ChildItem .\ | Where-Object { $_.PSIsContainer } | ForEach-Object { $_.GetType() }
# and
Get-ChildItem .\ | Where-Object { $_.PSIsContainer } | Get-Member
But be careful: You will get a lot of output on directories with many files and subdirectories.
Also have a look at the answer How can I exclude multiple folders using Get-ChildItem -exclude? on stackoverflow. This answer comes with lots of elegant solutions for PowerShell from v1.0 to v5.0.
problem was
$Exclude= "\\SomeServer\sqltestbackups\Archive" I needed to double up the \s to be
$Exclude= "\\\\SomeServer\\sqltestbackups\\Archive"
after doing this the script worked fine.

Powershell command to delete subfolders without deleting the root

I'm having problems to create a PS command that allows me to delete several subfolders without deleting the roof folder.
I.E:
C:\Test has many subfolders:
C:\Test\Item1
C:\Test\Item2
C:\Test\Item3
And the folders Item1, Item2 and Item3 have many subfolders and files.
I would like to create a PS that would allow me to delete all the empty subfolders inside Item1, Item2 and Item3 without deleting Item1, Item2 and Item3 folders. It is possible that any of the Item folders is empty, but I don't want to delete them, just the empty content of each folder.
This is just an example, I have a have around 300 Item folders inside Test.
I usually would use this:
$path="C:\TEST"
do {
$dir = gci $path -directory -recurse | Where { (gci $_.fullName).count -eq 0 } | select -expandproperty FullName
$dir | Foreach-Object { Remove-Item $_ }
} while ($dir.count -gt 0)
But this deletes the folder root folder (Item1, Item2 or Item3) if they are empty.
Thanks in advance.
So you are looking to delete All Items inside Empty Subfolders or all items in General?
This will delete all Folders or Items in General inside of the Directory "C:\abc\"
$path = "C:\abc\"
Get-ChildItem -Path $path -Recurse| Foreach-object {Remove-item -Recurse -path $_.FullName }
This will delete all Folders that dont have any items in them.
$path = "C:\abc\"
Get-ChildItem -Path $path -Recurse | Where-Object {(Get-ChildItem $_.FullName).Count -eq 0} |Foreach-object {Remove-item -Recurse -path $_.FullName }
ยด
This will look inside "C:\abc\" Get all the children and delete all empty Directories inside the Children in your example this would be Item1,Item2,...
$Path = "C:\abc\"
$itemFolders= Get-ChildItem -Path $Path
$itemFolders| Foreach-Object {
Get-ChildItem -Path $_.FullName |
Where-Object {(Get-ChildItem $_.FullName).Count -eq 0} |
Foreach-object {Remove-item -Recurse -path $_.FullName }
}
Just a quick and dirty bit of Code as I dont have much time, hope I could be of help.
EDIT: Here is what i came up with, its not as performant as I'd like but it gets the job done and is fairly quick, try it out for yourself it worked for me - even threw in a couple of comments and output to clarify what's going on.
$Path="C:\abc\"
$itemFolders = Get-ChildItem $Path
#Get All Folders inside
$AllFolders = $itemFolders | Get-ChildItem -Recurse | Where-Object {$_.PSIsContainer} | Select -Property FullName
#First delete all files older than 30 days
$itemFolders | Get-ChildItem -Recurse -File | ForEach-Object{
$limit = (Get-Date).AddDays(-30)
if($_.LastWriteTime -lt $limit)
{
"{0} hasn't been modified in the last 30 days deleting it" -f $_.FullName
Remove-Item -Path $_.FullName -Force -ErrorAction SilentlyContinue
}
}
#Check if there are files inside that are not containers
$AllFolders | ForEach-Object{
$files = Get-ChildItem -File -Recurse -Path $_.FullName
$directories = Get-ChildItem -Directory -Recurse -Path $_.FullName
#If There are any files inside the folder dont delete it.
if($files.Count -gt 0)
{
"Found {0} files inside {1} do not delete this" -f $files.Count, $_.FullName
}
#If There are no files and no directories inside delete it.
elseif($files.Count -eq 0 -and $directories.Count -eq 0)
{
"Empty Folder {0} deleting it" -f $_.FullName
Remove-Item -Path $_.FullName -Recurse -Force -ErrorAction SilentlyContinue
}
#If there are no files and empty directories inside delete it.
elseif($files.Count -eq 0 -and $directories.Count -gt 0)
{
"No Files but directories found in {0} since its recursive delete it" -f $_.FullName
Remove-Item -Path $_.FullName -Recurse -Force -ErrorAction SilentlyContinue
}
}

Compare-Object Delete File if file does not exist on source

I have this PowerShell code that compares 2 directories and removes files if the files no longer exist in the source directory.
For example say I have Folder 1 & Folder 2. I want to compare Folder 1 with Folder 2, If a file doesn't exist anymore in Folder 1 it will remove it from Folder 2.
this code works ok but I have a problem where it also picks up file differences on the date/time. I only want it to pick up a difference if the file doesn't exist anymore in Folder 1.
Compare-Object $source $destination -Property Name -PassThru | Where-Object {$_.SideIndicator -eq "=>"} | % {
if(-not $_.FullName.PSIsContainer) {
UPDATE-LOG "File: $($_.FullName) has been removed from source"
Remove-Item -Path $_.FullName -Force -ErrorAction SilentlyContinue
}
}
Is there an extra Where-Object {$file1 <> $file2} or something like that.?
I am not sure how you are getting the information for $source and $destination I am assuming you are using Get-ChildItem
What i would do to eliminate the issue with date/time would be to not capture it in these variables. For Example:
$source = Get-ChildItem C:\temp\Folder1 -Recurse | select -ExpandProperty FullName
$destination = Get-ChildItem C:\temp\Folder2 -Recurse | select -ExpandProperty FullName
By doing this you only get the FullName Property for each object that is a child item not the date/time.
You would need to change some of the script after doing this for it to still work.
If I am not getting it wrong, the issue is your code is deleting the file with different time-stamp as compared to source:
Did you try -ExcludeProperty?
$source = Get-ChildItem "E:\New folder" -Recurse | select -ExcludeProperty Date
The following script can serve your purpose
$Item1=Get-ChildItem 'SourcePath'
$Item2=Get-ChildItem 'DestinationPath'
$DifferenceItem=Compare-Object $Item1 $Item2
$ItemToBeDeleted=$DifferenceItem | where {$_.SideIndicator -eq "=>" }
foreach ($item in $ItemToBeDeleted)
{
$FullPath=$item.InputObject.FullName
Remove-Item $FullPath -Force
}
Try something like this
In PowerShell V5:
$yourdir1="c:\temp"
$yourdir2="c:\temp2"
$filesnamedir1=(gci $yourdir1 -file).Name
gci $yourdir2 -file | where Name -notin $filesnamedir1| remove-item
In old PowerShell:
$yourdir1="c:\temp"
$yourdir2="c:\temp2"
$filesnamedir1=(gci $yourdir1 | where {$_.psiscontainer -eq $false}).Name
gci $yourdir2 | where {$_.psiscontainer -eq $false -and $_.Name -notin $filesnamedir1} | remove-item
If you want to compare files in multiple dir, use the -recurse option for every gci command.

How to recursively remove all empty folders in PowerShell?

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 script with no success.
Could you please tell me how to fix it?
$tdc='C:\a\c\d\'
$a = Get-ChildItem $tdc -recurse | Where-Object {$_.PSIsContainer -eq $True}
$a | Where-Object {$_.GetFiles().Count -eq 0} | Select-Object FullName
I am using PowerShell on Windows 8.1 version.
You need to keep a few key things in mind when looking at a problem like this:
Get-ChildItem -Recurse performs head recursion, meaning it returns folders as soon as it finds them when walking through a tree. 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.
Get-ChildItem does not return hidden files or folders by default. As a result you need to take extra steps to ensure that you don't remove folders that appear empty but that contain hidden files or folders. Get-Item and Get-ChildItem both have a -Force parameter which can be used to retrieve hidden files or folders as well as visible files or folders.
With those points in mind, here is a solution that uses tail recursion and that properly tracks hidden files or folders, making sure to remove hidden folders if they are empty and also making sure to keep folders that may contain one or more hidden files.
First this is the script block (anonymous function) that does the job:
# A script block (anonymous function) that will remove empty folders
# under a root folder, using tail-recursion to ensure that it only
# walks the folder tree once. -Force is used to be able to process
# hidden files/folders as well.
$tailRecursion = {
param(
$Path
)
foreach ($childDirectory in Get-ChildItem -Force -LiteralPath $Path -Directory) {
& $tailRecursion -Path $childDirectory.FullName
}
$currentChildren = Get-ChildItem -Force -LiteralPath $Path
$isEmpty = $currentChildren -eq $null
if ($isEmpty) {
Write-Verbose "Removing empty folder at path '${Path}'." -Verbose
Remove-Item -Force -LiteralPath $Path
}
}
If you want to test it here's code that will create interesting test data (make sure you don't already have a folder c:\a because it will be deleted):
# This creates some test data under C:\a (make sure this is not
# a directory you care about, because this will remove it if it
# exists). This test data contains a directory that is hidden
# that should be removed as well as a file that is hidden in a
# directory that should not be removed.
Remove-Item -Force -Path C:\a -Recurse
New-Item -Force -Path C:\a\b\c\d -ItemType Directory > $null
$hiddenFolder = Get-Item -Force -LiteralPath C:\a\b\c
$hiddenFolder.Attributes = $hiddenFolder.Attributes -bor [System.IO.FileAttributes]::Hidden
New-Item -Force -Path C:\a\b\e -ItemType Directory > $null
New-Item -Force -Path C:\a\f -ItemType Directory > $null
New-Item -Force -Path C:\a\f\g -ItemType Directory > $null
New-Item -Force -Path C:\a\f\h -ItemType Directory > $null
Out-File -Force -FilePath C:\a\f\test.txt -InputObject 'Dummy file'
Out-File -Force -FilePath C:\a\f\h\hidden.txt -InputObject 'Hidden file'
$hiddenFile = Get-Item -Force -LiteralPath C:\a\f\h\hidden.txt
$hiddenFile.Attributes = $hiddenFile.Attributes -bor [System.IO.FileAttributes]::Hidden
Here's how you use it. Note that this will remove the top folder (the C:\a folder in this example, which gets created if you generated the test data using the script above) if that folder winds up being empty after deleting all empty folders under it.
& $tailRecursion -Path 'C:\a'
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)
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.
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
ls c:\temp -rec |%{ if ($_.PSIsContainer -eq $True) {if ( (ls $_.fullname -rec | measure |select -expand count ) -eq "0" ){ ri $_.fullname -whatif} } }
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} }
If you just want to make sure, that you delete only folders that may contain subfolders but no files within itself and its subfolders, this may be an easier an quicker way.
$Empty = Get-ChildItem $Folder -Directory -Recurse |
Where-Object {(Get-ChildItem $_.FullName -File -Recurse -Force).Count -eq 0}
Foreach ($Dir in $Empty)
{
if (test-path $Dir.FullName)
{Remove-Item -LiteralPath $Dir.FullName -recurse -force}
}
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
I have adapted the script of RichardHowells.
It doesn't delete the folder if there is a thumbs.db.
##############
# Parameters #
##############
param(
$Chemin = "" , # Path to clean
$log = "" # Logs path
)
###########
# Process #
###########
if (($Chemin -eq "") -or ($log-eq "") ){
Write-Error 'Parametres non reseignes - utiliser la syntaxe : -Chemin "Argument" -log "argument 2" ' -Verbose
Exit
}
#loging
$date = get-date -format g
Write-Output "begining of cleaning folder : $chemin at $date" >> $log
Write-Output "------------------------------------------------------------------------------------------------------------" >> $log
<########################################################################
define a script block that will remove empty folders under a root folder,
using tail-recursion to ensure that it only walks the folder tree once.
-Force is used to be able to process hidden files/folders as well.
########################################################################>
$tailRecursion = {
param(
$Path
)
foreach ($childDirectory in Get-ChildItem -Force -LiteralPath $Path -Directory) {
& $tailRecursion -Path $childDirectory.FullName
}
$currentChildren = Get-ChildItem -Force -LiteralPath $Path
Write-Output $childDirectory.FullName
<# Suppression des fichiers Thumbs.db #>
Foreach ( $file in $currentchildren )
{
if ($file.name -notmatch "Thumbs.db"){break}
if ($file.name -match "Thumbs.db"){
Remove-item -force -LiteralPath $file.FullName}
}
$currentChildren = Get-ChildItem -Force -LiteralPath $Path
$isEmpty = $currentChildren -eq $null
if ($isEmpty) {
$date = get-date -format g
Write-Output "Removing empty folder at path '${Path}'. $date" >> $log
Remove-Item -Force -LiteralPath $Path
}
}
# Invocation of the script block
& $tailRecursion -Path $Chemin
#loging
$date = get-date -format g
Write-Output "End of cleaning folder : $chemin at $date" >> $log
Write-Output "------------------------------------------------------------------------------------------------------------" >> $log
Something like this works for me. The script delete empty folders and folders containing only folder (no files, no hidden files).
$items = gci -LiteralPath E:\ -Directory -Recurse
$dirs = [System.Collections.Generic.HashSet[string]]::new([string[]]($items |% FullName))
for (;;) {
$remove = $dirs |? { (gci -LiteralPath $_ -Force).Count -eq 0 }
if ($remove) {
$remove | rm
$dirs.ExceptWith( [string[]]$remove )
}
else {
break
}
}
I wouldn't take the comments/1st post to heart unless you also want to delete files that are nested more than one folder deep. You are going to end up deleting directories that may contain directories that may contain files. This is better:
$FP= "C:\Temp\"
$dirs= Get-Childitem -LiteralPath $FP -directory -recurse
$Empty= $dirs | Where-Object {$_.GetFiles().Count -eq 0 **-and** $_.GetDirectories().Count -eq 0} |
Select-Object FullName
The above checks to make sure the directory is in fact empty whereas the OP only checks to make sure there are no files. That in turn would result in files nexted a few folders deep also being deleted.
You may need to run the above a few times as it won't delete Dirs that have nested Dirs. So it only deletes the deepest level. So loop it until they're all gone.
Something else I do not do is use the -force parameter. That is by design. If in fact remove-item hits a dir that is not empty you want to be prompted as an additional safety.
$files = Get-ChildItem -Path c:\temp -Recurse -Force | where psiscontainer ; [array]::reverse($files)
[Array]::reverse($files) will reverse your items, so you get the lowest files in hierarchy first.
I use this to manipulate filenames that have too long filepaths, before I delete them.
This is a simple approach
dir -Directory | ? { (dir $_).Count -eq 0 } | Remove-Item
This will remove up all empty folders in the specified directory $tdc.
It is also a lot faster since there's no need for multiple runs.
$tdc = "x:\myfolder" # Specify the root folder
gci $tdc -Directory -Recurse `
| Sort-Object { $_.FullName.Length } -Descending `
| ? { $_.GetFiles().Count -eq 0 } `
| % {
if ($_.GetDirectories().Count -eq 0) {
Write-Host " Removing $($_.FullName)"
$_.Delete()
}
}
#By Mike Mike Costa Rica
$CarpetasVacias = Get-ChildItem -Path $CarpetaVer -Recurse -Force -Directory | Where {(gci $_.fullName).count -eq 0} | select Fullname,Name,LastWriteTime
$TotalCarpetas = $CarpetasVacias.Count
$CountSu = 1
ForEach ($UnaCarpeta in $CarpetasVacias){
$RutaCarp = $UnaCarpeta.Fullname
Remove-Item -Path $RutaCarp -Force -Confirm:$False -ErrorAction Ignore
$testCar = Test-Path $RutaCarp
if($testCar -eq $true){
$Datem = (Get-Date).tostring("MM-dd-yyyy HH:mm:ss")
Write-Host "$Datem ---> $CountSu de $TotalCarpetas Carpetas Error Borrando Directory: $RutaCarp" -foregroundcolor "red"
}else{
$Datem = (Get-Date).tostring("MM-dd-yyyy HH:mm:ss")
Write-Host "$Datem ---> $CountSu de $TotalCarpetas Carpetas Correcto Borrando Directory: $RutaCarp" -foregroundcolor "gree"
}
$CountSu += 1
}