refactor(windows scripts): remove BOM and improve UTF-8 handling

This commit is contained in:
2026-09-09 15:48:32 -04:00
parent d27bf49c40
commit bdeb9c767a
19 changed files with 148 additions and 89 deletions
+11 -8
View File
@@ -1,4 +1,4 @@
<#
<#
LXS - Disk Cleanup (Windows)
Description: Reclaim disk space (temp files, Windows Update cache, recycle
bin, Prefetch, error reports, WinSxS component store).
@@ -23,7 +23,10 @@ if ($LxsLibPath -and (Test-Path $LxsLibPath)) {
Write-Error 'Failed to fetch lib/common.ps1'
exit 1
}
. ([scriptblock]::Create($LxsLibSource))
# The library ships with a UTF-8 BOM (Windows PowerShell 5.1 needs it to
# read the file as UTF-8); over HTTP that BOM arrives as a leading U+FEFF
# character, which the parser will not accept.
. ([scriptblock]::Create(($LxsLibSource -replace '^\uFEFF', '')))
}
$env:LXS_LOG_FILE = Join-Path (Get-LxsTempDir) 'lxs_cleanup.log'
@@ -80,14 +83,14 @@ function Get-LxsFolderSize {
}
# 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.
# 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)"
Write-Host "$($script:Gray) $Label - not present, skipped$($script:NC)"
return 0
}
$before = Get-LxsFolderSize -Path $Path
@@ -102,7 +105,7 @@ function Clear-LxsFolderContents {
$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"
Write-LxsOk "$Label - $(Format-LxsSize $freed) reclaimed$suffix"
return $freed
}
@@ -170,7 +173,7 @@ if (-not $NoRecycleBin) {
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)"
Write-Host "$($script:Gray) Recycle bin - already empty or not accessible$($script:NC)"
}
}
@@ -187,14 +190,14 @@ if ($doUpdateCache) {
$stopped += $svc
}
} catch {
Write-LxsWarn "Could not stop $svc the update cache may be partially locked."
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." }
try { Start-Service -Name $svc -ErrorAction Stop } catch { Write-LxsWarn "Failed to restart $svc - start it manually." }
}
}
}
+13 -10
View File
@@ -1,4 +1,4 @@
<#
<#
LXS - Debloat and privacy (Windows)
Description: Remove preinstalled apps you pick, and turn off telemetry,
Cortana, ads, tracking tasks, Copilot and Recall.
@@ -27,7 +27,10 @@ if ($LxsLibPath -and (Test-Path $LxsLibPath)) {
Write-Error 'Failed to fetch lib/common.ps1'
exit 1
}
. ([scriptblock]::Create($LxsLibSource))
# The library ships with a UTF-8 BOM (Windows PowerShell 5.1 needs it to
# read the file as UTF-8); over HTTP that BOM arrives as a leading U+FEFF
# character, which the parser will not accept.
. ([scriptblock]::Create(($LxsLibSource -replace '^\uFEFF', '')))
}
$env:LXS_LOG_FILE = Join-Path (Get-LxsTempDir) 'lxs_debloat.log'
@@ -57,9 +60,9 @@ function Assert-LxsCheckpoint {
# ═══════════════════════════════════════════════════════════════════════════
# Preinstalled apps
#
# Only packages on this list are ever offered. Anything not listed here
# Only packages on this list are ever offered. Anything not listed here -
# including the Store, App Installer (winget), Terminal, Defender UI, the
# VCLibs/.NET runtime frameworks and every driver package is left alone,
# VCLibs/.NET runtime frameworks and every driver package - is left alone,
# because removing those breaks Windows or LXS itself.
# ═══════════════════════════════════════════════════════════════════════════
@@ -152,13 +155,13 @@ function Remove-LxsSelectedApps {
Write-Host ''
Write-Host "$($script:Gray)Enter the numbers to remove, separated by commas (e.g. 1,4,7),$($script:NC)"
Write-Host "$($script:Gray)or 'all' for every entry above. Empty cancels nothing is removed$($script:NC)"
Write-Host "$($script:Gray)or 'all' for every entry above. Empty cancels - nothing is removed$($script:NC)"
Write-Host "$($script:Gray)unless you name it here.$($script:NC)"
Write-Host ''
$selection = Read-Host -Prompt 'Selection'
if ([string]::IsNullOrWhiteSpace($selection)) {
Write-LxsInfo 'Cancelled nothing removed.'
Write-LxsInfo 'Cancelled - nothing removed.'
return
}
@@ -347,7 +350,7 @@ function Invoke-LxsTweakGroup {
Disable-ScheduledTask -TaskName $leaf -TaskPath $parent -ErrorAction Stop | Out-Null
$disabled++
} catch {
# Absent on this edition/build nothing to disable.
# Absent on this edition/build - nothing to disable.
}
}
Write-LxsOk "$disabled tracking scheduled task(s) disabled"
@@ -360,7 +363,7 @@ function Restore-LxsTweaks {
Write-Host 'This restores every registry value, service and scheduled task that'
Write-Host 'this script changes, back to the Windows default.'
Write-Host ''
Write-Host "$($script:Gray)Apps you removed are NOT restored reinstall them from the Store.$($script:NC)"
Write-Host "$($script:Gray)Apps you removed are NOT restored - reinstall them from the Store.$($script:NC)"
Write-Host ''
if (-not (Confirm-LxsAction -Question 'Revert the privacy tweaks?')) {
Write-LxsInfo 'Cancelled.'
@@ -374,13 +377,13 @@ function Restore-LxsTweaks {
if (Test-Path $e.Path) {
Remove-ItemProperty -Path $e.Path -Name $e.Name -ErrorAction Stop
}
Write-LxsOk "$($e.Label) policy removed"
Write-LxsOk "$($e.Label) - policy removed"
} catch {
# Already absent: that is the default state we wanted.
}
} else {
if (Set-LxsRegistryValue -Path $e.Path -Name $e.Name -Value $e.Default) {
Write-LxsOk "$($e.Label) restored to default"
Write-LxsOk "$($e.Label) - restored to default"
}
}
}
+9 -6
View File
@@ -1,4 +1,4 @@
<#
<#
LXS - Harden Windows
Description: Apply a baseline security posture (firewall, SMBv1, UAC,
Defender, LLMNR/NetBIOS) and audit local accounts.
@@ -24,7 +24,10 @@ if ($LxsLibPath -and (Test-Path $LxsLibPath)) {
Write-Error 'Failed to fetch lib/common.ps1'
exit 1
}
. ([scriptblock]::Create($LxsLibSource))
# The library ships with a UTF-8 BOM (Windows PowerShell 5.1 needs it to
# read the file as UTF-8); over HTTP that BOM arrives as a leading U+FEFF
# character, which the parser will not accept.
. ([scriptblock]::Create(($LxsLibSource -replace '^\uFEFF', '')))
}
$env:LXS_LOG_FILE = Join-Path (Get-LxsTempDir) 'lxs_harden.log'
@@ -266,7 +269,7 @@ if ($DoNameResolution) {
}
# ═══════════════════════════════════════════════════════════════════════════
# Account audit reports only, changes nothing.
# Account audit - reports only, changes nothing.
# ═══════════════════════════════════════════════════════════════════════════
Write-Host ''
@@ -279,7 +282,7 @@ try {
foreach ($a in $admins) { Write-Host " - $($a.Name) $($script:Gray)($($a.ObjectClass), $($a.PrincipalSource))$($script:NC)" }
if ($admins.Count -gt 2) {
Write-Host ''
Write-LxsWarn "$($admins.Count) accounts have administrator rights review whether they all need it."
Write-LxsWarn "$($admins.Count) accounts have administrator rights - review whether they all need it."
}
} catch {
Write-LxsWarn "Could not enumerate the Administrators group: $($_.Exception.Message)"
@@ -301,7 +304,7 @@ try {
Write-Host "$($script:Gray) Password never expires: $($enabledNoExpiry.Name -join ', ')$($script:NC)"
}
if ($guest -and $guest.Enabled) {
Write-LxsWarn 'The Guest account is ENABLED disable it unless you rely on it.'
Write-LxsWarn 'The Guest account is ENABLED - disable it unless you rely on it.'
} else {
Write-LxsOk 'Guest account is disabled'
}
@@ -318,7 +321,7 @@ Show-LxsSeparator
Write-Host ''
Write-LxsOk "$changes hardening change(s) applied"
if ($problems -gt 0) {
Write-LxsWarn "$problems change(s) failed see the messages above."
Write-LxsWarn "$problems change(s) failed - see the messages above."
}
if ($DoSmb) {
Write-LxsWarn 'Reboot to finish removing the SMBv1 client feature.'
+6 -3
View File
@@ -1,4 +1,4 @@
<#
<#
LXS - Tools index (Windows)
Description: Interactive menu listing the scripts in windows\tools
Repo: https://git.hyko.cx/hykocx/lxs
@@ -19,7 +19,10 @@ if ($LxsLibPath -and (Test-Path $LxsLibPath)) {
Write-Error 'Failed to fetch lib/common.ps1'
exit 1
}
. ([scriptblock]::Create($LxsLibSource))
# The library ships with a UTF-8 BOM (Windows PowerShell 5.1 needs it to
# read the file as UTF-8); over HTTP that BOM arrives as a leading U+FEFF
# character, which the parser will not accept.
. ([scriptblock]::Create(($LxsLibSource -replace '^\uFEFF', '')))
}
function Show-LxsToolsMenu {
@@ -66,7 +69,7 @@ function Show-LxsToolsMenu {
$code = Invoke-LxsSibling -RelativePath $script -SelfDirectory $PSScriptRoot
# Exit code 75 from a child means it already paused on its own
# (its own "Back" or end-of-run prompt) skip the redundant prompt.
# (its own "Back" or end-of-run prompt) - skip the redundant prompt.
if ($code -ne 75) { Read-LxsEnter }
}
}
+7 -3
View File
@@ -1,4 +1,4 @@
<#
<#
LXS - Network Diagnostics (Windows)
Description: Inspect and repair the network stack.
Everything is read-only except option 8 (stack reset).
@@ -20,7 +20,10 @@ if ($LxsLibPath -and (Test-Path $LxsLibPath)) {
Write-Error 'Failed to fetch lib/common.ps1'
exit 1
}
. ([scriptblock]::Create($LxsLibSource))
# The library ships with a UTF-8 BOM (Windows PowerShell 5.1 needs it to
# read the file as UTF-8); over HTTP that BOM arrives as a leading U+FEFF
# character, which the parser will not accept.
. ([scriptblock]::Create(($LxsLibSource -replace '^\uFEFF', '')))
}
$env:LXS_LOG_FILE = Join-Path (Get-LxsTempDir) 'lxs_net_diag.log'
@@ -51,7 +54,8 @@ function Show-LxsListeningPorts {
Write-Host ''
try {
$rows = Get-NetTCPConnection -State Listen -ErrorAction Stop | ForEach-Object {
$procName = try { (Get-Process -Id $_.OwningProcess -ErrorAction Stop).ProcessName } catch { 'unknown' }
$procName = 'unknown'
try { $procName = (Get-Process -Id $_.OwningProcess -ErrorAction Stop).ProcessName } catch { $procName = 'unknown' }
[pscustomobject]@{
Local = "$($_.LocalAddress):$($_.LocalPort)"
Port = $_.LocalPort
+9 -6
View File
@@ -1,6 +1,6 @@
<#
<#
LXS - Remote Access (Windows)
Description: Remote Desktop and the OpenSSH server status, enable,
Description: Remote Desktop and the OpenSSH server - status, enable,
disable. The Windows counterpart of root-ssh-login.sh.
Repo: https://git.hyko.cx/hykocx/lxs
#>
@@ -20,7 +20,10 @@ if ($LxsLibPath -and (Test-Path $LxsLibPath)) {
Write-Error 'Failed to fetch lib/common.ps1'
exit 1
}
. ([scriptblock]::Create($LxsLibSource))
# The library ships with a UTF-8 BOM (Windows PowerShell 5.1 needs it to
# read the file as UTF-8); over HTTP that BOM arrives as a leading U+FEFF
# character, which the parser will not accept.
. ([scriptblock]::Create(($LxsLibSource -replace '^\uFEFF', '')))
}
$env:LXS_LOG_FILE = Join-Path (Get-LxsTempDir) 'lxs_remote_access.log'
@@ -60,7 +63,7 @@ function Show-LxsRemoteStatus {
if (Get-LxsNlaState) {
Write-LxsOk 'Network Level Authentication is on'
} else {
Write-LxsWarn 'Network Level Authentication is OFF turn it on (option 2).'
Write-LxsWarn 'Network Level Authentication is OFF - turn it on (option 2).'
}
try {
$port = (Get-ItemProperty -Path $LxsRdpTcpKey -Name 'PortNumber' -ErrorAction Stop).PortNumber
@@ -123,7 +126,7 @@ function Enable-LxsRdp {
Write-LxsWarn "Could not enable the firewall rules: $($_.Exception.Message)"
}
Write-Host ''
Write-LxsWarn 'Only expose RDP to the internet behind a VPN it is a constant brute-force target.'
Write-LxsWarn 'Only expose RDP to the internet behind a VPN - it is a constant brute-force target.'
}
function Disable-LxsRdp {
@@ -255,7 +258,7 @@ function Set-LxsSshDefaultShell {
default { $null }
}
if (-not $shell) {
if ($choice -eq '2') { Write-LxsErr 'pwsh.exe was not found install PowerShell 7 first.' }
if ($choice -eq '2') { Write-LxsErr 'pwsh.exe was not found - install PowerShell 7 first.' }
else { Write-LxsInfo 'Cancelled.' }
return
}
+5 -2
View File
@@ -1,4 +1,4 @@
<#
<#
LXS - System Repair (Windows)
Description: Integrity repair for the component store and system files
(SFC, DISM, chkdsk). Requires administrator rights.
@@ -20,7 +20,10 @@ if ($LxsLibPath -and (Test-Path $LxsLibPath)) {
Write-Error 'Failed to fetch lib/common.ps1'
exit 1
}
. ([scriptblock]::Create($LxsLibSource))
# The library ships with a UTF-8 BOM (Windows PowerShell 5.1 needs it to
# read the file as UTF-8); over HTTP that BOM arrives as a leading U+FEFF
# character, which the parser will not accept.
. ([scriptblock]::Create(($LxsLibSource -replace '^\uFEFF', '')))
}
$env:LXS_LOG_FILE = Join-Path (Get-LxsTempDir) 'lxs_repair.log'
+6 -3
View File
@@ -1,4 +1,4 @@
<#
<#
LXS - System Restore Point (Windows)
Description: Enable System Protection, create checkpoints, list them and
roll back. The safety net for harden.ps1 and debloat.ps1.
@@ -20,7 +20,10 @@ if ($LxsLibPath -and (Test-Path $LxsLibPath)) {
Write-Error 'Failed to fetch lib/common.ps1'
exit 1
}
. ([scriptblock]::Create($LxsLibSource))
# The library ships with a UTF-8 BOM (Windows PowerShell 5.1 needs it to
# read the file as UTF-8); over HTTP that BOM arrives as a leading U+FEFF
# character, which the parser will not accept.
. ([scriptblock]::Create(($LxsLibSource -replace '^\uFEFF', '')))
}
$env:LXS_LOG_FILE = Join-Path (Get-LxsTempDir) 'lxs_restore_point.log'
@@ -70,7 +73,7 @@ function Show-LxsProtectionStatus {
Write-Host " Shadow storage: $usedGB GB used of $maxGB GB allocated"
}
} else {
Write-LxsWarn "System Protection looks OFF for $drive no restore points can be created."
Write-LxsWarn "System Protection looks OFF for $drive - no restore points can be created."
}
} catch {
Write-LxsWarn 'Could not read the shadow storage configuration.'
+8 -5
View File
@@ -1,4 +1,4 @@
<#
<#
LXS - System Infos (Windows)
Description: Essential system monitoring and diagnostic views.
Read-only: nothing here changes the machine.
@@ -20,7 +20,10 @@ if ($LxsLibPath -and (Test-Path $LxsLibPath)) {
Write-Error 'Failed to fetch lib/common.ps1'
exit 1
}
. ([scriptblock]::Create($LxsLibSource))
# The library ships with a UTF-8 BOM (Windows PowerShell 5.1 needs it to
# read the file as UTF-8); over HTTP that BOM arrives as a leading U+FEFF
# character, which the parser will not accept.
. ([scriptblock]::Create(($LxsLibSource -replace '^\uFEFF', '')))
}
$env:LXS_LOG_FILE = Join-Path (Get-LxsTempDir) 'lxs_system_infos.log'
@@ -63,7 +66,7 @@ function Show-LxsSystemInformation {
ForEach-Object { "$($_.Name) (driver $($_.DriverVersion))" })
if ($gpu.Count -gt 0) { Write-LxsField 'GPU' $gpu }
} catch {
# Headless or restricted WMI not worth failing the whole view.
# Headless or restricted WMI - not worth failing the whole view.
}
# Activation status. LicenseStatus 1 = licensed.
@@ -81,7 +84,7 @@ function Show-LxsSystemInformation {
6 { 'Extended grace' }
default { 'Unknown' }
}
Write-LxsField 'Activation' "$state $($lic[0].Name)"
Write-LxsField 'Activation' "$state - $($lic[0].Name)"
}
} catch {
# SoftwareLicensingProduct is unavailable on some SKUs.
@@ -172,7 +175,7 @@ function Show-LxsEventLogs {
Show-LxsBoxTop -Title 'SYSTEM LOGS' -Right 'LAST 50'
Write-Host ''
foreach ($logName in @('System', 'Application')) {
Write-Host "$($script:Cyan)$($script:Bold)$logName errors and warnings$($script:NC)"
Write-Host "$($script:Cyan)$($script:Bold)$logName - errors and warnings$($script:NC)"
try {
$events = Get-WinEvent -FilterHashtable @{ LogName = $logName; Level = 1, 2, 3 } -MaxEvents 25 -ErrorAction Stop
$events | Format-Table -AutoSize -Wrap `
+5 -2
View File
@@ -1,4 +1,4 @@
<#
<#
LXS - Update Windows
Description: Install pending Windows updates and upgrade every winget
package. Mirror of linux/tools/update-server.sh.
@@ -22,7 +22,10 @@ if ($LxsLibPath -and (Test-Path $LxsLibPath)) {
Write-Error 'Failed to fetch lib/common.ps1'
exit 1
}
. ([scriptblock]::Create($LxsLibSource))
# The library ships with a UTF-8 BOM (Windows PowerShell 5.1 needs it to
# read the file as UTF-8); over HTTP that BOM arrives as a leading U+FEFF
# character, which the parser will not accept.
. ([scriptblock]::Create(($LxsLibSource -replace '^\uFEFF', '')))
}
$env:LXS_LOG_FILE = Join-Path (Get-LxsTempDir) 'lxs_update_windows.log'