Files
BakNRet/Backup.ps1
T
Shuery 4e3c0461c0 refactor: 入口改名(Backup/Restore -> Backup-Data/Restore-Data)+ 只留一轮的垫片
git mv 保留历史。旧名字留薄垫片:入口脚本是**外部接口**(README 二十多处引用、使用者的肌肉记忆、注册脚本里的路径),内部实现改名断了会当场报错,外部接口改名断了是静默没用 —— 后者对备份工具尤其不能接受(ADR-0012)。

垫片踩到四个坑,全部由门禁报出(E2E 与 Pester 集成用例本来就是通过子进程调这两个入口的,于是它们原封不动成了垫片的验收):① `& script.ps1` 里子脚本的 exit 不会把退出码传到父脚本的 $LASTEXITCODE —— 会把失败变成成功,而计划任务靠退出码判断成败;② 调用方传 -Verbose 时垫片里的 Import-Module 会多打一行加载信息,顶掉测试的输出断言;③ 不重定向时子进程的输出到不了调用方(父进程的 stdout 常常是管道,而 .NET 起的进程默认只继承控制台)—— 改成显式重定向 + 异步读转发(同步先读 stdout 再读 stderr 会在管道写满时死锁);④ **`[CmdletBinding()]` 会把 -Verbose 当通用参数绑走,它不会落进 $Rest,于是没被转发给子进程** —— 而测试正是靠 -Verbose 拿那条 VERBOSE 级日志的。现在显式把 -Verbose / -Debug 加进转发参数。

参数引号化用模块自己的 ConvertTo-BakNRetNativeArgumentString(5.1 上 ProcessStartInfo.ArgumentList 不存在)。tools\Register-BackupTask.ps1 已同步改指 Backup-Data.ps1(4 处)。

门禁新增一条机械检查:**源码里不得出现"左边空的赋值"** —— 它是"用双引号拼代码导致 $变量 被插值成空"那个坑的指纹(合法语法、Parse 层抓不到、只有跑到才炸,同一块代码里出现过五次)。垫片全部用单引号 here-string 生成,零插值。

验收:test.ps1 9/9 全绿(5.1 与 7);真实清单只读冒烟 4/4。
2026-09-27 19:51:24 +08:00

64 lines
3.3 KiB
PowerShell

<#
.SYNOPSIS
已改名:本脚本只是转发到 Backup-Data.ps1(这一层只保留一轮)。
.DESCRIPTION
为什么留一层转发(ADR-0012):入口脚本是**外部接口** —— README 里有二十多处引用、有使用者的
肌肉记忆、tools\Register-BackupTask.ps1 里也可能已经注册过这个路径。内部实现改名断了会当场
报错;外部接口改名断了是**静默没用**,而备份工具"静默没用"是最不能接受的失败方式。
为什么用子进程、而不是 `& $target`:实测 `& script.ps1` 里子脚本的 exit **不会**把退出码传到
父脚本的 $LASTEXITCODE —— 垫片会让失败变成"成功"(错配置时返回 0,被调用脚本返回 1),而计划
任务正是靠退出码判断成败。
为什么重定向之后要**自己转发**:父进程的 stdout 常常是管道(测试与使用者的管道都在解析入口的
输出),而 .NET 起的进程默认只继承控制台、不继承那个管道 —— 不重定向时子进程的输出就到不了
调用方(实测红过)。所以显式重定向,再用**异步读**把两个流读出来转发(同步先读 stdout 再读
stderr 会在管道写满时死锁)。代价是 stdout/stderr 的相对顺序不再保留 —— 这也正是这层垫片
只留一轮的原因之一。
为什么导入模块时临时压掉 verbose:调用方可能给入口传 -Verbose(测试就是这么拿到详细日志的),
那样 Import-Module 会多打一行 "VERBOSE: Loading module from path ..." —— 而测试是**解析子进程
输出**做断言的,多这么一行就会把它顶掉。只压这一句,$Rest 里的 -Verbose 仍会原样转发。
这一层下一轮删。想用新名字就直接调 Backup-Data.ps1。
#>
[CmdletBinding()]
param(
[Parameter(ValueFromRemainingArguments = $true)]$Rest
)
$ErrorActionPreference = 'Stop'
$savedVerbosePreference = $VerbosePreference
$VerbosePreference = 'SilentlyContinue'
Import-Module (Join-Path $PSScriptRoot 'BakNRet\BakNRet.psd1') -Force
$VerbosePreference = $savedVerbosePreference
$target = Join-Path $PSScriptRoot 'Backup-Data.ps1'
Write-Host '注意:Backup.ps1 已改名为 Backup-Data.ps1(这层转发只保留一轮)。' -ForegroundColor Yellow
$hostExe = (Get-Process -Id $PID).Path
$forwardArguments = @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $target) + @($Rest)+ @(if ($PSBoundParameters.ContainsKey('Verbose')) { '-Verbose' })+ @(if ($PSBoundParameters.ContainsKey('Debug')) { '-Debug' })
$startInfo = New-Object System.Diagnostics.ProcessStartInfo
$startInfo.FileName = $hostExe
$startInfo.Arguments = ConvertTo-BakNRetNativeArgumentString -ArgumentList $forwardArguments
$startInfo.UseShellExecute = $false
$startInfo.RedirectStandardOutput = $true
$startInfo.RedirectStandardError = $true
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $startInfo
[void]$process.Start()
$stdoutTask = $process.StandardOutput.ReadToEndAsync()
$stderrTask = $process.StandardError.ReadToEndAsync()
$process.WaitForExit()
$standardOutput = $stdoutTask.Result
$standardError = $stderrTask.Result
if ($standardOutput) { Write-Host -NoNewline $standardOutput }
if ($standardError) { [Console]::Error.Write($standardError) }
exit $process.ExitCode