fix(cleanup-windows): ASCII-only damit Windows-PowerShell 5.1 parsen kann
Windows-PowerShell 5.1 liest .ps1 ohne UTF-8 BOM als Windows-1252;
em-dashes (-), Pfeile (->), Box-Zeichen, Emojis (OK/FAIL/WARN) wurden
als Mojibake interpretiert ("â€"" fuer "-") und sprengten den Parser.
Loesung: alle Sonderzeichen durch ASCII-Aequivalente ersetzt:
- "-" / "->" statt em-dash / Pfeil
- "===" statt Box-Linien
- "[OK]" / "[FAIL]" / "[WARN]" statt Emojis
Funktioniert jetzt zuverlaessig auf jeder Windows-PS-Version ohne BOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
607a4c9ff8
commit
60c5cb7e59
|
|
@ -1,7 +1,7 @@
|
||||||
@echo off
|
@echo off
|
||||||
REM ════════════════════════════════════════════════════════════════
|
REM ================================================================
|
||||||
REM ARIA — Cleanup-Wrapper fuer Windows
|
REM ARIA - Cleanup-Wrapper fuer Windows
|
||||||
REM ════════════════════════════════════════════════════════════════
|
REM ================================================================
|
||||||
REM Ruft cleanup-windows.ps1 mit ExecutionPolicy Bypass auf.
|
REM Ruft cleanup-windows.ps1 mit ExecutionPolicy Bypass auf.
|
||||||
REM Funktioniert auch wenn Windows .ps1 direkt nicht startet.
|
REM Funktioniert auch wenn Windows .ps1 direkt nicht startet.
|
||||||
REM
|
REM
|
||||||
|
|
@ -11,6 +11,6 @@ REM cleanup-windows.bat stefan -SkipPrune
|
||||||
REM
|
REM
|
||||||
REM Doppelklick funktioniert NICHT (braucht Username als Param).
|
REM Doppelklick funktioniert NICHT (braucht Username als Param).
|
||||||
REM Per Konsole aufrufen.
|
REM Per Konsole aufrufen.
|
||||||
REM ════════════════════════════════════════════════════════════════
|
REM ================================================================
|
||||||
|
|
||||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0cleanup-windows.ps1" %*
|
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0cleanup-windows.ps1" %*
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# ════════════════════════════════════════════════════════════════
|
# ================================================================
|
||||||
# ARIA — Windows / WSL2 / Docker Desktop VHDX Cleanup
|
# ARIA - Windows / WSL2 / Docker Desktop VHDX Cleanup
|
||||||
# ════════════════════════════════════════════════════════════════
|
# ================================================================
|
||||||
#
|
#
|
||||||
# Findet alle WSL2 + Docker Desktop ext4.vhdx Files unter
|
# Findet alle WSL2 + Docker Desktop ext4.vhdx Files unter
|
||||||
# C:\Users\<USER>\AppData\Local\... und kompaktiert sie via diskpart.
|
# C:\Users\<USER>\AppData\Local\... und kompaktiert sie via diskpart.
|
||||||
|
|
@ -8,10 +8,11 @@
|
||||||
# Containern geloescht hast (z.B. nach `docker system prune`),
|
# Containern geloescht hast (z.B. nach `docker system prune`),
|
||||||
# der aber von der VHDX bisher nicht freigegeben wurde.
|
# der aber von der VHDX bisher nicht freigegeben wurde.
|
||||||
#
|
#
|
||||||
# Nutzung (PowerShell als ADMIN):
|
# Nutzung (PowerShell als ADMIN, oder via cleanup-windows.bat):
|
||||||
# .\cleanup-windows.ps1 stefan
|
# .\cleanup-windows.ps1 stefan
|
||||||
# .\cleanup-windows.ps1 -User stefan
|
# .\cleanup-windows.ps1 -User stefan
|
||||||
# .\cleanup-windows.ps1 -User stefan -SkipPrune # nur compacten
|
# .\cleanup-windows.ps1 -User stefan -SkipPrune # nur compacten
|
||||||
|
# .\cleanup-windows.ps1 -User stefan -PruneOnly # nur prune
|
||||||
#
|
#
|
||||||
# Was passiert:
|
# Was passiert:
|
||||||
# 1. Erst (optional): docker system prune + builder prune in WSL2
|
# 1. Erst (optional): docker system prune + builder prune in WSL2
|
||||||
|
|
@ -19,7 +20,11 @@
|
||||||
# 3. Alle gefundenen .vhdx Files mit diskpart compact vdisk shrinken
|
# 3. Alle gefundenen .vhdx Files mit diskpart compact vdisk shrinken
|
||||||
#
|
#
|
||||||
# Hinweis: diskpart braucht KEINE Hyper-V Tools (anders als Optimize-VHD).
|
# Hinweis: diskpart braucht KEINE Hyper-V Tools (anders als Optimize-VHD).
|
||||||
# ════════════════════════════════════════════════════════════════
|
#
|
||||||
|
# ASCII-only damit Windows-PowerShell 5.1 das File ohne BOM korrekt
|
||||||
|
# parsen kann (UTF-8-Sonderzeichen wuerden sonst als Windows-1252
|
||||||
|
# fehlinterpretiert).
|
||||||
|
# ================================================================
|
||||||
|
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param(
|
param(
|
||||||
|
|
@ -27,27 +32,27 @@ param(
|
||||||
HelpMessage="Dein Windows-Benutzername (z.B. stefan)")]
|
HelpMessage="Dein Windows-Benutzername (z.B. stefan)")]
|
||||||
[string]$User,
|
[string]$User,
|
||||||
|
|
||||||
[Parameter(HelpMessage="Docker prune ueberspringen — nur compacten")]
|
[Parameter(HelpMessage="Docker prune ueberspringen - nur compacten")]
|
||||||
[switch]$SkipPrune,
|
[switch]$SkipPrune,
|
||||||
|
|
||||||
[Parameter(HelpMessage="Docker prune NUR machen, dann beenden")]
|
[Parameter(HelpMessage="Docker prune NUR machen, dann beenden")]
|
||||||
[switch]$PruneOnly
|
[switch]$PruneOnly
|
||||||
)
|
)
|
||||||
|
|
||||||
# Defensive: Process-Scope ExecutionPolicy auf Bypass — verhindert dass
|
# Defensive: Process-Scope ExecutionPolicy auf Bypass - verhindert dass
|
||||||
# Untersaetze (z.B. Module) blockiert werden. Harmless wenn Parent schon
|
# Untersaetze (z.B. Module) blockiert werden. Harmless wenn Parent schon
|
||||||
# Bypass aufgerufen hat.
|
# Bypass aufgerufen hat.
|
||||||
try { Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force | Out-Null } catch {}
|
try { Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force | Out-Null } catch {}
|
||||||
|
|
||||||
# Admin-Check + Self-Elevation
|
# Admin-Check + Self-Elevation
|
||||||
# Wenn nicht als Admin gestartet → einmal neu starten als Admin, mit
|
# Wenn nicht als Admin gestartet -> einmal neu starten als Admin, mit
|
||||||
# ExecutionPolicy Bypass + den Original-Argumenten. User muss nur einmal
|
# ExecutionPolicy Bypass + den Original-Argumenten. User muss nur einmal
|
||||||
# UAC-Prompt bestaetigen.
|
# UAC-Prompt bestaetigen.
|
||||||
$isAdmin = ([Security.Principal.WindowsPrincipal] `
|
$isAdmin = ([Security.Principal.WindowsPrincipal] `
|
||||||
[Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(
|
[Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(
|
||||||
[Security.Principal.WindowsBuiltInRole]::Administrator)
|
[Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||||
if (-not $isAdmin) {
|
if (-not $isAdmin) {
|
||||||
Write-Host "→ Starte neu als Administrator (mit ExecutionPolicy Bypass)..." -ForegroundColor Yellow
|
Write-Host "-> Starte neu als Administrator (mit ExecutionPolicy Bypass)..." -ForegroundColor Yellow
|
||||||
$myPath = $MyInvocation.MyCommand.Path
|
$myPath = $MyInvocation.MyCommand.Path
|
||||||
$forwardArgs = @("-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "`"$myPath`"")
|
$forwardArgs = @("-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "`"$myPath`"")
|
||||||
if ($User) { $forwardArgs += @("-User", $User) }
|
if ($User) { $forwardArgs += @("-User", $User) }
|
||||||
|
|
@ -56,8 +61,8 @@ if (-not $isAdmin) {
|
||||||
try {
|
try {
|
||||||
Start-Process powershell.exe -Verb RunAs -ArgumentList $forwardArgs
|
Start-Process powershell.exe -Verb RunAs -ArgumentList $forwardArgs
|
||||||
} catch {
|
} catch {
|
||||||
Write-Host "❌ UAC-Elevation abgebrochen oder fehlgeschlagen." -ForegroundColor Red
|
Write-Host "[FAIL] UAC-Elevation abgebrochen oder fehlgeschlagen." -ForegroundColor Red
|
||||||
Write-Host " Rechtsklick auf PowerShell → 'Als Administrator ausfuehren'" -ForegroundColor Yellow
|
Write-Host " Rechtsklick auf PowerShell -> 'Als Administrator ausfuehren'" -ForegroundColor Yellow
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
exit 0
|
exit 0
|
||||||
|
|
@ -65,17 +70,17 @@ if (-not $isAdmin) {
|
||||||
|
|
||||||
$basePath = "C:\Users\$User\AppData\Local"
|
$basePath = "C:\Users\$User\AppData\Local"
|
||||||
if (-not (Test-Path $basePath)) {
|
if (-not (Test-Path $basePath)) {
|
||||||
Write-Host "❌ Pfad existiert nicht: $basePath" -ForegroundColor Red
|
Write-Host "[FAIL] Pfad existiert nicht: $basePath" -ForegroundColor Red
|
||||||
Write-Host " Pruefe den Benutzernamen." -ForegroundColor Yellow
|
Write-Host " Pruefe den Benutzernamen." -ForegroundColor Yellow
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan
|
Write-Host "================================================================" -ForegroundColor Cyan
|
||||||
Write-Host " ARIA Cleanup fuer User: $User" -ForegroundColor Cyan
|
Write-Host " ARIA Cleanup fuer User: $User" -ForegroundColor Cyan
|
||||||
Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan
|
Write-Host "================================================================" -ForegroundColor Cyan
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
|
|
||||||
# ── 1. Docker prune (in WSL2) ──────────────────────────────────
|
# -- 1. Docker prune (in WSL2) -----------------------------------
|
||||||
if (-not $SkipPrune) {
|
if (-not $SkipPrune) {
|
||||||
Write-Host "[1/3] Docker Cleanup in WSL2..." -ForegroundColor Yellow
|
Write-Host "[1/3] Docker Cleanup in WSL2..." -ForegroundColor Yellow
|
||||||
Write-Host " docker system prune -a --volumes -f" -ForegroundColor Gray
|
Write-Host " docker system prune -a --volumes -f" -ForegroundColor Gray
|
||||||
|
|
@ -83,26 +88,26 @@ if (-not $SkipPrune) {
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
try {
|
try {
|
||||||
wsl -e bash -c "docker system prune -a --volumes -f && docker builder prune -a -f"
|
wsl -e bash -c "docker system prune -a --volumes -f && docker builder prune -a -f"
|
||||||
Write-Host " ✅ fertig" -ForegroundColor Green
|
Write-Host " [OK] fertig" -ForegroundColor Green
|
||||||
} catch {
|
} catch {
|
||||||
Write-Host " ⚠️ Docker prune fehlgeschlagen (vielleicht laeuft Docker Desktop nicht?)" -ForegroundColor Yellow
|
Write-Host " [WARN] Docker prune fehlgeschlagen (vielleicht laeuft Docker Desktop nicht?)" -ForegroundColor Yellow
|
||||||
Write-Host " $_" -ForegroundColor Gray
|
Write-Host " $_" -ForegroundColor Gray
|
||||||
}
|
}
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
if ($PruneOnly) {
|
if ($PruneOnly) {
|
||||||
Write-Host "─PruneOnly gesetzt — fertig." -ForegroundColor Cyan
|
Write-Host "PruneOnly gesetzt - fertig." -ForegroundColor Cyan
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── 2. WSL2 shutdown ──────────────────────────────────────────
|
# -- 2. WSL2 shutdown --------------------------------------------
|
||||||
Write-Host "[2/3] WSL2 herunterfahren..." -ForegroundColor Yellow
|
Write-Host "[2/3] WSL2 herunterfahren..." -ForegroundColor Yellow
|
||||||
wsl --shutdown
|
wsl --shutdown
|
||||||
Start-Sleep -Seconds 3
|
Start-Sleep -Seconds 3
|
||||||
Write-Host " ✅ fertig" -ForegroundColor Green
|
Write-Host " [OK] fertig" -ForegroundColor Green
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
|
|
||||||
# ── 3. VHDX-Files finden + compacten ──────────────────────────
|
# -- 3. VHDX-Files finden + compacten ----------------------------
|
||||||
Write-Host "[3/3] VHDX-Files suchen + compacten..." -ForegroundColor Yellow
|
Write-Host "[3/3] VHDX-Files suchen + compacten..." -ForegroundColor Yellow
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
|
|
||||||
|
|
@ -126,7 +131,7 @@ Write-Host ""
|
||||||
$totalBefore = ($vhdxFiles | Measure-Object Length -Sum).Sum
|
$totalBefore = ($vhdxFiles | Measure-Object Length -Sum).Sum
|
||||||
|
|
||||||
foreach ($f in $vhdxFiles) {
|
foreach ($f in $vhdxFiles) {
|
||||||
Write-Host "→ Compact: $($f.FullName)" -ForegroundColor White
|
Write-Host "-> Compact: $($f.FullName)" -ForegroundColor White
|
||||||
$sizeBefore = [math]::Round($f.Length / 1GB, 2)
|
$sizeBefore = [math]::Round($f.Length / 1GB, 2)
|
||||||
|
|
||||||
# Temporaeres diskpart-Script schreiben
|
# Temporaeres diskpart-Script schreiben
|
||||||
|
|
@ -141,26 +146,26 @@ exit
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$output = & diskpart /s $tmp 2>&1
|
$output = & diskpart /s $tmp 2>&1
|
||||||
# Datei neu lesen — Length ist gecacht
|
# Datei neu lesen - Length ist gecacht
|
||||||
$newFile = Get-Item $f.FullName
|
$newFile = Get-Item $f.FullName
|
||||||
$sizeAfter = [math]::Round($newFile.Length / 1GB, 2)
|
$sizeAfter = [math]::Round($newFile.Length / 1GB, 2)
|
||||||
$saved = [math]::Round($sizeBefore - $sizeAfter, 2)
|
$saved = [math]::Round($sizeBefore - $sizeAfter, 2)
|
||||||
if ($saved -gt 0) {
|
if ($saved -gt 0) {
|
||||||
Write-Host (" ✅ {0} GB → {1} GB (gespart: {2} GB)" -f $sizeBefore, $sizeAfter, $saved) -ForegroundColor Green
|
Write-Host (" [OK] {0} GB -> {1} GB (gespart: {2} GB)" -f $sizeBefore, $sizeAfter, $saved) -ForegroundColor Green
|
||||||
} else {
|
} else {
|
||||||
Write-Host (" ─ {0} GB → {1} GB (nichts zu holen — File war schon optimal)" -f $sizeBefore, $sizeAfter) -ForegroundColor DarkGray
|
Write-Host (" -- {0} GB -> {1} GB (nichts zu holen - File war schon optimal)" -f $sizeBefore, $sizeAfter) -ForegroundColor DarkGray
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
Write-Host " ❌ Fehler: $_" -ForegroundColor Red
|
Write-Host " [FAIL] Fehler: $_" -ForegroundColor Red
|
||||||
Write-Host " diskpart-Output:" -ForegroundColor DarkGray
|
Write-Host " diskpart-Output:" -ForegroundColor DarkGray
|
||||||
$output | ForEach-Object { Write-Host " $_" -ForegroundColor DarkGray }
|
$output | ForEach-Object { Write-Host " $_" -ForegroundColor DarkGray }
|
||||||
} finally {
|
} finally {
|
||||||
Remove-Item $tmp -ErrorAction SilentlyContinue
|
Remove-Item $tmp -ErrorAction SilentlyContinue
|
||||||
}
|
}
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Zusammenfassung ─────────────────────────────────────────
|
# -- Zusammenfassung ---------------------------------------------
|
||||||
$vhdxFilesAfter = @()
|
$vhdxFilesAfter = @()
|
||||||
$vhdxFilesAfter += Get-ChildItem -Path "$basePath\Docker" -Recurse -Filter "*.vhdx" -ErrorAction SilentlyContinue
|
$vhdxFilesAfter += Get-ChildItem -Path "$basePath\Docker" -Recurse -Filter "*.vhdx" -ErrorAction SilentlyContinue
|
||||||
$vhdxFilesAfter += Get-ChildItem -Path "$basePath\Packages" -Recurse -Filter "ext4.vhdx" -ErrorAction SilentlyContinue
|
$vhdxFilesAfter += Get-ChildItem -Path "$basePath\Packages" -Recurse -Filter "ext4.vhdx" -ErrorAction SilentlyContinue
|
||||||
|
|
@ -169,11 +174,11 @@ $totalAfter = ($vhdxFilesAfter | Measure-Object Length -Sum).Sum
|
||||||
|
|
||||||
$savedTotal = [math]::Round(($totalBefore - $totalAfter) / 1GB, 2)
|
$savedTotal = [math]::Round(($totalBefore - $totalAfter) / 1GB, 2)
|
||||||
|
|
||||||
Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan
|
Write-Host "================================================================" -ForegroundColor Cyan
|
||||||
Write-Host (" Gesamt: {0} GB → {1} GB (gespart: {2} GB)" -f `
|
Write-Host (" Gesamt: {0} GB -> {1} GB (gespart: {2} GB)" -f `
|
||||||
[math]::Round($totalBefore / 1GB, 2),
|
[math]::Round($totalBefore / 1GB, 2),
|
||||||
[math]::Round($totalAfter / 1GB, 2),
|
[math]::Round($totalAfter / 1GB, 2),
|
||||||
$savedTotal) -ForegroundColor Cyan
|
$savedTotal) -ForegroundColor Cyan
|
||||||
Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan
|
Write-Host "================================================================" -ForegroundColor Cyan
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
Write-Host "Fertig. Docker Desktop / WSL2 starten ja von alleine wieder beim naechsten Aufruf." -ForegroundColor Green
|
Write-Host "Fertig. Docker Desktop / WSL2 starten ja von alleine wieder beim naechsten Aufruf." -ForegroundColor Green
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue