fix: 让 5.1 真正可用(显式编码 + 原生 stderr 处理 + .psd1 夹具带 BOM)

上一提交让 5.1 能解析源码,但 Unit 与 Smoke 在 5.1 上仍然是红的。根因是三类彼此
无关的 5.1/7 行为差,全部实测确认:

1) 不写 -Encoding 时,5.1 的 Get-Content / Set-Content 默认是 ANSI,7 是 UTF-8。
   症状是 UTF-8 字节被按 GBK 解出「璇存槑」这类乱码。62 处补上显式 -Encoding UTF8。
   用 AST 而不是正则定位,避免把注释里的散文也改掉。

2) .psd1 夹具用无 BOM 写,而引擎的 .psd1 读取器(Import-PowerShellDataFile)只能靠
   BOM 判断编码、没有参数可传,于是 5.1 按 ANSI 解。40 处夹具改为带 BOM 写 —— 这正是
   .editorconfig 里 [*.psd1] charset = utf-8-bom 本来就要求的,是夹具违反了自己的约定。
   .cmd 批次文件刻意保持无 BOM:cmd.exe 会被 BOM 弄坏。

3) 5.1 在 $ErrorActionPreference = Stop 下会把原生命令写到 stderr 的内容升级成终止性
   NativeCommandError,7 改了这条。takeown/icacls 的 ACL 复位调用、以及 test.ps1 自己
   调子进程的地方,都需要在 Continue 下跑。

验收:test.ps1 9/9 全绿(Encode + Parse + Unit + Smoke + E2E,在 7 与 5.1 上各跑一遍)。

已知未处理(留待后续提交):tools/lab/** 里还有若干「原生命令 + 2>&1 + Stop」的同类
写法(takeown / icacls / scoop / code / Get-WimInfo)。它们要 Hyper-V 实验机才跑得到,
不在验收门槛内。
This commit is contained in:
Shuery committed 2026-09-26 22:16:04 +08:00
1 parent 2d26f78d15
commit e10503be76
13 files changed
+122 -104

No files matched your search

+19 -5
View File
@@ -97,8 +97,22 @@ BeforeAll {
<# .SYNOPSIS 先把 ACL 复位再删:拒绝型 / protected 的 DACL 会让 Remove-Item 直接失败。 #>
param([Parameter(Mandatory = $true)][string]$Path)
if (-not (Test-Path -LiteralPath $Path)) { return }
& takeown.exe /F $Path /R /D Y 2>&1 | Out-Null
& icacls.exe $Path /reset /T /C /Q 2>&1 | Out-Null
# 这两个是原生命令,失败与否都不该打断测试 —— 它们的唯一目的是"把 ACL 复位到能删"。
#
# 必须在 Continue 下调用:Windows PowerShell 5.1 在 $ErrorActionPreference = 'Stop'
# 时,会把原生命令写到 stderr 的内容升级成终止性的 NativeCommandError 抛出来
# (takeown / icacls 对"已经处理过"的对象就会写 stderr)。PowerShell 7 改了这条
# 规矩,所以这段在 7 上一直是绿的,只在 5.1 上炸。
$previousPreference = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
try {
& takeown.exe /F $Path /R /D Y 2>&1 | Out-Null
& icacls.exe $Path /reset /T /C /Q 2>&1 | Out-Null
} finally {
$ErrorActionPreference = $previousPreference
}
Remove-Item -LiteralPath $Path -Recurse -Force -ErrorAction SilentlyContinue
}
@@ -160,7 +174,7 @@ BeforeAll {
New-Item -ItemType Directory -Path $backupDir -Force | Out-Null
$listPath = Join-Path $dir 'BackupList.txt'
[System.IO.File]::WriteAllText($listPath, "$sourcePath`n", [System.Text.UTF8Encoding]::new($false))
[System.IO.File]::WriteAllText($listPath, "$sourcePath`n", [System.Text.UTF8Encoding]::new($true))
$configPath = Join-Path $dir 'BackupConfig.psd1'
$configText = @"
@@ -180,7 +194,7 @@ BeforeAll {
DefaultExcludes = @()
}
"@
[System.IO.File]::WriteAllText($configPath, $configText, [System.Text.UTF8Encoding]::new($false))
[System.IO.File]::WriteAllText($configPath, $configText, [System.Text.UTF8Encoding]::new($true))
return [pscustomobject]@{
Dir = $dir
@@ -426,7 +440,7 @@ Describe '与 Backup.ps1 / Restore.ps1 的集成' -Skip:(-not $script:HasSevenZi
$sidecars.Count | Should -Be 1
$result.Output | Should -Match '安全描述符:3 个对象'
$manifest = Get-Content -LiteralPath (Join-Path $script:Harness.BackupDir 'manifest.json') -Raw | ConvertFrom-Json
$manifest = Get-Content -Encoding UTF8 -LiteralPath (Join-Path $script:Harness.BackupDir 'manifest.json') -Raw | ConvertFrom-Json
$key = @($manifest.items.PSObject.Properties.Name)[0]
$manifest.items.$key.security.file | Should -Be $sidecars[0].Name
$manifest.items.$key.security.objects | Should -Be 3