public/Invoke-OctaDiskCleanup.ps1
|
function Get-OctaCleanupTargets { <# .SYNOPSIS Measures real, current reclaimable bytes per target (FR-045). Never caches/estimates. #> [CmdletBinding()] param() function Measure-OctaPathBytes { param([string[]]$Paths) $total = 0L foreach ($p in $Paths) { $items = Get-ChildItem -Path $p -Recurse -Force -ErrorAction SilentlyContinue $total += ($items | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum } return $total } $targets = @() $targets += [pscustomobject]@{ Name = 'User temp files'; Paths = @("$env:TEMP\*") MeasuredBytes = (Measure-OctaPathBytes -Paths @("$env:TEMP\*")) RiskLevel = 'Safe'; Reversible = $false IrreversibleReason = 'Deleted temp files are not recoverable.' } $targets += [pscustomobject]@{ Name = 'System temp files'; Paths = @("$env:WINDIR\Temp\*") MeasuredBytes = (Measure-OctaPathBytes -Paths @("$env:WINDIR\Temp\*")) RiskLevel = 'Safe'; Reversible = $false IrreversibleReason = 'Deleted temp files are not recoverable.' } $targets += [pscustomobject]@{ Name = 'Prefetch'; Paths = @("$env:WINDIR\Prefetch\*.pf") MeasuredBytes = (Measure-OctaPathBytes -Paths @("$env:WINDIR\Prefetch\*.pf")) RiskLevel = 'Safe'; Reversible = $false IrreversibleReason = 'Windows regenerates prefetch data as needed; deleted files are not restored.' } $targets += [pscustomobject]@{ Name = 'Thumbnail cache'; Paths = @("$env:LOCALAPPDATA\Microsoft\Windows\Explorer\thumbcache_*.db") MeasuredBytes = (Measure-OctaPathBytes -Paths @("$env:LOCALAPPDATA\Microsoft\Windows\Explorer\thumbcache_*.db")) RiskLevel = 'Safe'; Reversible = $false IrreversibleReason = 'Thumbnails are regenerated on demand; deleted cache files are not restored.' } $targets += [pscustomobject]@{ Name = 'Windows Update leftovers'; Paths = @("$env:WINDIR\SoftwareDistribution\Download\*") MeasuredBytes = (Measure-OctaPathBytes -Paths @("$env:WINDIR\SoftwareDistribution\Download\*")) RiskLevel = 'Safe'; Reversible = $false IrreversibleReason = 'Windows re-downloads update files if needed again.' } $targets += [pscustomobject]@{ Name = 'Crash dumps'; Paths = @("$env:WINDIR\Minidump\*", "$env:WINDIR\memory.dmp") MeasuredBytes = (Measure-OctaPathBytes -Paths @("$env:WINDIR\Minidump\*", "$env:WINDIR\memory.dmp")) RiskLevel = 'Safe'; Reversible = $false IrreversibleReason = 'Crash dump files are diagnostic-only; deleting them loses that diagnostic history.' } $recycleBinBytes = 0L try { $shell = New-Object -ComObject Shell.Application $recycleBin = $shell.Namespace(10) $recycleBinBytes = ($recycleBin.Items() | ForEach-Object { $_.Size } | Measure-Object -Sum).Sum } catch { $recycleBinBytes = 0L } $targets += [pscustomobject]@{ Name = 'Recycle Bin'; Paths = @('(Recycle Bin)') MeasuredBytes = $recycleBinBytes RiskLevel = 'Safe'; Reversible = $false IrreversibleReason = 'Emptying the Recycle Bin permanently deletes its contents.' } $windowsOldPath = "$env:SystemDrive\Windows.old" if (Test-Path $windowsOldPath) { $targets += [pscustomobject]@{ Name = 'Old Windows installation (Windows.old)'; Paths = @($windowsOldPath) MeasuredBytes = (Measure-OctaPathBytes -Paths @("$windowsOldPath\*")) RiskLevel = 'Risky'; Reversible = $false IrreversibleReason = 'Removing this forfeits the ability to roll back a Windows feature upgrade.' } } return $targets } function Invoke-OctaDiskCleanup { <# .SYNOPSIS Dry-run and (optionally) apply disk cleanup across known, standard Windows locations. FR-045/FR-046. Attempts a System Restore Point before deleting anything, even though the deletions themselves are not undo-able the way a registry action is. #> [CmdletBinding()] param( [switch]$Apply, [switch]$Yes ) $targets = Get-OctaCleanupTargets $totalBytes = ($targets | Measure-Object -Property MeasuredBytes -Sum).Sum Write-Host 'Disk Cleanup targets (all irreversible except where noted):' foreach ($t in $targets) { $gb = [Math]::Round($t.MeasuredBytes / 1GB, 2) Write-Host (" {0,-40} {1,8} GB [{2}]" -f $t.Name, $gb, $t.RiskLevel) } Write-Host ("Total reclaimable: {0} GB" -f [Math]::Round($totalBytes / 1GB, 2)) if (-not $Apply) { return [pscustomobject]@{ Status = 'DryRun'; Targets = $targets; TotalBytes = $totalBytes } } $safeTargets = @($targets | Where-Object { $_.RiskLevel -ne 'Risky' }) $riskyTargets = @($targets | Where-Object { $_.RiskLevel -eq 'Risky' }) if (-not $Yes) { $response = Read-Host 'Delete these files? (y/N)' if ($response -notin @('y', 'Y', 's', 'S')) { return [pscustomobject]@{ Status = 'Cancelled' } } } $applyTargets = $safeTargets if ($riskyTargets.Count -gt 0) { Write-Host 'Risky targets (confirm separately):' foreach ($t in $riskyTargets) { Write-Host " $($t.Name)" } if ($Yes) { $applyTargets += $riskyTargets } else { $riskyResponse = Read-Host 'Delete these too? (y/N)' if ($riskyResponse -in @('y', 'Y', 's', 'S')) { $applyTargets += $riskyTargets } } } New-OctaRestorePoint -Description 'Octa disk cleanup' | Out-Null $freedBytes = 0L foreach ($t in $applyTargets) { if ($t.Name -eq 'Recycle Bin') { Clear-RecycleBin -Force -ErrorAction SilentlyContinue } else { foreach ($p in $t.Paths) { Remove-Item -Path $p -Recurse -Force -ErrorAction SilentlyContinue } } $freedBytes += $t.MeasuredBytes } return [pscustomobject]@{ Status = 'Success'; FreedBytes = $freedBytes } } |