docs: Update README and documentation for lxs multi-tool across Linux and Windows platforms.

This commit is contained in:
2026-09-09 15:32:05 -04:00
parent 3fda22d3d5
commit 1df5cdb0bb
37 changed files with 4811 additions and 75 deletions
+227
View File
@@ -0,0 +1,227 @@
<#
LXS - Disk Cleanup (Windows)
Description: Reclaim disk space (temp files, Windows Update cache, recycle
bin, Prefetch, error reports, WinSxS component store).
Repo: https://git.hyko.cx/hykocx/lxs
Usage: cleanup.ps1 [-Yes] [-NoUpdateCache] [-NoRecycleBin] [-NoPrefetch]
[-NoComponentCleanup] [-Help]
#>
# Load LXS common library (colors, UI helpers, spinner, loggers, guards).
# Prefers the sibling ..\lib\common.ps1 (repo checkout or installed layout) and
# only hits the network when this script is run standalone.
$LxsRawBase = if ($env:LXS_RAW_BASE) { $env:LXS_RAW_BASE } else { 'https://git.hyko.cx/hykocx/lxs/raw/branch/main' }
if (-not $env:LXS_RAW_PLATFORM_BASE) { $env:LXS_RAW_PLATFORM_BASE = "$LxsRawBase/windows" }
$LxsLibPath = if ($PSScriptRoot) { Join-Path $PSScriptRoot '..\lib\common.ps1' } else { $null }
if ($LxsLibPath -and (Test-Path $LxsLibPath)) {
. $LxsLibPath
} else {
try {
$LxsLibSource = Invoke-RestMethod -Uri "$env:LXS_RAW_PLATFORM_BASE/lib/common.ps1" -UseBasicParsing -ErrorAction Stop
} catch {
Write-Error 'Failed to fetch lib/common.ps1'
exit 1
}
. ([scriptblock]::Create($LxsLibSource))
}
$env:LXS_LOG_FILE = Join-Path (Get-LxsTempDir) 'lxs_cleanup.log'
$LxsOpts = Read-LxsFlags -Arguments $args -Known @(
'-Yes', '-y', '-NoUpdateCache', '-NoRecycleBin', '-NoPrefetch',
'-NoComponentCleanup', '-Help', '-h'
)
if (Show-LxsUnknownFlags $LxsOpts) { exit 1 }
$Yes = Test-LxsFlag $LxsOpts @('-Yes', '-y')
$NoUpdateCache = Test-LxsFlag $LxsOpts @('-NoUpdateCache')
$NoRecycleBin = Test-LxsFlag $LxsOpts @('-NoRecycleBin')
$NoPrefetch = Test-LxsFlag $LxsOpts @('-NoPrefetch')
$NoComponentCleanup = Test-LxsFlag $LxsOpts @('-NoComponentCleanup')
$Help = Test-LxsFlag $LxsOpts @('-Help', '-h')
if ($Help) {
Write-Host @'
Usage: cleanup.ps1 [options]
Options:
-Yes Skip the confirmation prompt
-NoUpdateCache Skip the Windows Update download cache
-NoRecycleBin Skip emptying the recycle bin
-NoPrefetch Skip the Prefetch folder
-NoComponentCleanup Skip DISM /StartComponentCleanup (the slow one)
-Help Show this help
'@
exit 0
}
function Get-LxsFreeBytes {
try { return (New-Object System.IO.DriveInfo "$env:SystemDrive\").AvailableFreeSpace } catch { return 0 }
}
function Format-LxsSize {
param([double]$Bytes)
if ($Bytes -ge 1GB) { return ('{0:N2} GB' -f ($Bytes / 1GB)) }
if ($Bytes -ge 1MB) { return ('{0:N1} MB' -f ($Bytes / 1MB)) }
return ('{0:N0} KB' -f ($Bytes / 1KB))
}
function Get-LxsFolderSize {
param([string]$Path)
if (-not (Test-Path $Path)) { return 0 }
try {
$sum = (Get-ChildItem -Path $Path -Recurse -Force -File -ErrorAction SilentlyContinue |
Measure-Object -Property Length -Sum).Sum
if ($null -eq $sum) { return 0 }
return $sum
} catch {
return 0
}
}
# Delete the *contents* of a folder, never the folder itself. Files locked by a
# running process are skipped silently — that is expected on a live system.
function Clear-LxsFolderContents {
param(
[Parameter(Mandatory = $true)][string]$Path,
[Parameter(Mandatory = $true)][string]$Label
)
if (-not (Test-Path $Path)) {
Write-Host "$($script:Gray) $Label — not present, skipped$($script:NC)"
return 0
}
$before = Get-LxsFolderSize -Path $Path
$skipped = 0
Get-ChildItem -Path $Path -Force -ErrorAction SilentlyContinue | ForEach-Object {
try {
Remove-Item -Path $_.FullName -Recurse -Force -ErrorAction Stop
} catch {
$skipped++
}
}
$after = Get-LxsFolderSize -Path $Path
$freed = [math]::Max(0, $before - $after)
$suffix = if ($skipped -gt 0) { " ($skipped item(s) in use, skipped)" } else { '' }
Write-LxsOk "$Label$(Format-LxsSize $freed) reclaimed$suffix"
return $freed
}
# ═══════════════════════════════════════════════════════════════════════════
# Plan
# ═══════════════════════════════════════════════════════════════════════════
if (-not (Assert-LxsWindows)) { exit 1 }
$isAdmin = Test-LxsAdmin
$doUpdateCache = (-not $NoUpdateCache) -and $isAdmin
$doPrefetch = (-not $NoPrefetch) -and $isAdmin
$doComponent = (-not $NoComponentCleanup) -and $isAdmin
Clear-Host
Show-LxsBoxTop -Title 'DISK CLEANUP'
Write-Host ''
Write-Host 'The following locations will be cleaned:'
Write-Host ' - User temp folder'
if ($isAdmin) { Write-Host ' - System temp folder (C:\Windows\Temp)' }
if (-not $NoRecycleBin) { Write-Host ' - Recycle bin (all drives)' }
if ($doUpdateCache) { Write-Host ' - Windows Update download cache' }
if ($doPrefetch) { Write-Host ' - Prefetch folder' }
if ($isAdmin) { Write-Host ' - Windows Error Reporting queue and Delivery Optimization cache' }
if ($doComponent) { Write-Host ' - WinSxS component store (DISM /StartComponentCleanup) - slow' }
Write-Host ''
if (-not $isAdmin) {
Write-LxsWarn 'Not running as administrator: system-wide locations are skipped.'
Write-Host "$($script:Gray) Re-run from an elevated terminal to clean them.$($script:NC)"
Write-Host ''
}
Show-LxsSeparator
Write-Host ''
if (-not (Confirm-LxsAction -Question 'Proceed with the cleanup?' -DefaultYes -AssumeYes:$Yes)) {
Write-LxsInfo 'Cancelled.'
exit 0
}
# ═══════════════════════════════════════════════════════════════════════════
# Run
# ═══════════════════════════════════════════════════════════════════════════
$freeBefore = Get-LxsFreeBytes
Write-Host ''
Write-LxsInfo "Free space before: $(Format-LxsSize $freeBefore)"
Write-Host ''
Clear-LxsFolderContents -Path (Get-LxsTempDir) -Label 'User temp' | Out-Null
if ($isAdmin) {
Clear-LxsFolderContents -Path (Join-Path $env:SystemRoot 'Temp') -Label 'System temp' | Out-Null
Clear-LxsFolderContents -Path (Join-Path $env:ProgramData 'Microsoft\Windows\WER\ReportQueue') -Label 'Error reports' | Out-Null
Clear-LxsFolderContents -Path (Join-Path $env:SystemRoot 'SoftwareDistribution\DeliveryOptimization') -Label 'Delivery Optimization' | Out-Null
}
if ($doPrefetch) {
Clear-LxsFolderContents -Path (Join-Path $env:SystemRoot 'Prefetch') -Label 'Prefetch' | Out-Null
}
if (-not $NoRecycleBin) {
try {
Clear-RecycleBin -Force -ErrorAction Stop
Write-LxsOk 'Recycle bin emptied'
} catch {
# Clear-RecycleBin throws when the bin is already empty.
Write-Host "$($script:Gray) Recycle bin — already empty or not accessible$($script:NC)"
}
}
if ($doUpdateCache) {
# The download cache can only be cleared with the update services stopped;
# they are restarted afterwards whether or not the deletion succeeded.
$services = @('wuauserv', 'bits')
$stopped = @()
foreach ($svc in $services) {
try {
$s = Get-Service -Name $svc -ErrorAction Stop
if ($s.Status -eq 'Running') {
Stop-Service -Name $svc -Force -ErrorAction Stop
$stopped += $svc
}
} catch {
Write-LxsWarn "Could not stop $svc — the update cache may be partially locked."
}
}
try {
Clear-LxsFolderContents -Path (Join-Path $env:SystemRoot 'SoftwareDistribution\Download') -Label 'Windows Update cache' | Out-Null
} finally {
foreach ($svc in $stopped) {
try { Start-Service -Name $svc -ErrorAction Stop } catch { Write-LxsWarn "Failed to restart $svc — start it manually." }
}
}
}
if ($doComponent) {
Write-Host ''
Write-LxsInfo 'Running DISM component cleanup (several minutes, no output until done)...'
$ok = Invoke-LxsSpinner -Message 'DISM /StartComponentCleanup' -FilePath 'dism.exe' `
-ArgumentList @('/Online', '/Cleanup-Image', '/StartComponentCleanup')
if (-not $ok) { Write-LxsWarn 'Component cleanup did not complete; see the log above.' }
}
# Windows.old is reported, never auto-deleted: removing it forfeits the
# ability to roll back a feature update.
$windowsOld = Join-Path $env:SystemDrive 'Windows.old'
if (Test-Path $windowsOld) {
Write-Host ''
Write-LxsWarn "$windowsOld exists ($(Format-LxsSize (Get-LxsFolderSize -Path $windowsOld)))."
Write-Host "$($script:Gray) It holds your previous Windows install. Removing it blocks rollback;$($script:NC)"
Write-Host "$($script:Gray) use Settings > System > Storage > Temporary files if you want it gone.$($script:NC)"
}
$freeAfter = Get-LxsFreeBytes
Write-Host ''
Show-LxsSeparator
Write-Host ''
Write-LxsOk "Free space after: $(Format-LxsSize $freeAfter)"
Write-LxsOk "Reclaimed: $(Format-LxsSize ([math]::Max(0, $freeAfter - $freeBefore)))"
Write-Host ''
exit 0