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 purged
12: 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 = 1
22: $path = "E:\LPR_pic\"
23: $days = 120
24:
25:
26: #$folders = Get-ChildItem $path -Recurse | ?{ $_.PSIsContainer } |Select-Object -ExpandProperty FullName
27: #$folders = Get-ChildItem $path -Recurse | WHERE { $_.PSIsContainer -match"Directory"} |Select-Object -ExpandProperty FullName
28:
29: # Populate an array with folders only
30: $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