Here is a technique to improve performance when you come across the task of deleting thousands of files located under many folders
This is a common way to get all the files meeting a criteria of “older than a specific date” from a specific folder including any sub folder if exists while filtering on a specific file extension. The file extensions can be modified or changed to *.* in order to work on all file extensions. The –recurse option is required to work on subfolders and for the file extension filtering.
1: $days = 15; 2: $files = Get-ChildItem $path -recurse -include *.bak | Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-$days) -and $_.PSIsContainer -eq $False}
While the above command is good for most cases it will perform poor in cases where the destination path has many folders with many files.
I came across a case where it took that command around 45 minutes just to complete and populate the $files variable before getting to the actual purge phase.
This is the option I ended up with after various tries and different approaches.
Its performed so much faster in may case where I had a file system with over 800,000 files to deal with and a complex directory structure.
The trick is using the dir command within a cmd window (“C:\Windows\System32\cmd.exe”) and the appropriate switches.
1: $folders = cmd.exe /c "dir E:\LPR_pic /s /b /ad";
The complete script
1: #Set-ExecutionPolicy RemoteSigned 2: 3: <# 4: 20160520 - Yaniv Etrogi 5: Purge files 6: 7: Paramters: 8: $path - The root path we work on recursevly 9: $delete_only_archived_files - 1 specifies that only files with the Archive attribute on will be purged 10: 0 specifies that all files will be purged (disregard Archive attribute) 11: $days - The number of retention days. Only files older than the (current date - $days) will be purged12: Note that this parameter is assigned multipple times
13: $do_delete - 1 specifies that purge takes place 14: 0 specifies only printing (no purge) 15: #> 16: 17: Set-Location C: 18: $Attribute = [io.fileattributes]::Archive 19: $UserInteractive = [Environment]::UserInteractive 20: $delete_only_archived_files = 1 21: $do_delete = 122: $path = "E:\LPR_pic\"
23: $days = 120 24: 25: 26: #$folders = Get-ChildItem $path -Recurse | ?{ $_.PSIsContainer } |Select-Object -ExpandProperty FullName27: #$folders = Get-ChildItem $path -Recurse | WHERE { $_.PSIsContainer -match"Directory"} |Select-Object -ExpandProperty FullName
28: 29: # Populate an array with folders only30: $folders = cmd.exe /c "dir E:\LPR_pic /s /b /ad";
31: 32: 33: # Loop over the folders array 34: foreach($folder in $folders) 35: { 36: # If the folder is empty delete it 37: if ( (Get-ChildItem $folder | Measure-Object).Count -eq 0) 38: {39: if ($UserInteractive -eq "True" ) {Write-Host 'Deleting folder...' $folder -ForegroundColor Red}
40: Remove-Item -force -Recurse $folder; 41: } 42: else 43: {44: if ($UserInteractive -eq "True" ) {Write-Host 'Processing folder...' $folder -ForegroundColor Green}
45: $files = Get-ChildItem $folder | Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-$days) -and $_.PSIsContainer -eq $False} 46: 47: # Loop over the files in each folder 48: foreach($file in $files) 49: { 50: if ($do_delete -eq 1) 51: { 52: if ($UserInteractive -eq "True" ) {Write-Host -ForegroundColor White $file.FullName }
53: $file.Delete(); 54: } 55: else 56: { 57: if ($UserInteractive -eq "True" ) {Write-Host -ForegroundColor Yellow $file.FullName } 58: } 59: } 60: 61: } 62: } 63: 64: Latest posts by Yaniv Etrogi (see all)
- Monitor AlwaysOn Availabilty Groups - July 6, 2023
- SQL Server – The secret index syntax - February 8, 2023
- Use .net SqlClient with Powershell to access data - January 25, 2023
- Use Powershell to find unused resources in Azure - August 5, 2022
