Files
BakNRet/tools/Register-BackupTask.ps1
T
Shuery 2d26f78d15 fix: 源文件改存 UTF-8 with BOM,让 Windows PowerShell 5.1 真正可用
改造前:全仓 6/6 个源文件在 5.1 上解析失败(README 却承诺支持 5.1)。原因是文件是无 BOM 的
UTF-8,而 5.1 没有 BOM 就按 ANSI 代码页解码源码,中文变乱码、全角问号吃掉引号,整块语法塌掉。
现在 28/28 个文件在 5.1 与 7 上都解析零错误,E2E 36 项在 5.1 上全绿。

顺带修掉一个被 5.1 掩盖的缺陷:带 [CmdletBinding()] 的脚本在 5.1 上,param() 默认值里
拿不到 $PSScriptRoot(实测为空串,7 上正常)。于是 Backup.ps1 / Restore.ps1 在 5.1 上不传
路径参数就报错退出 —— 而计划任务恰恰不传。E2E 之所以看不见,是因为它总是显式传路径。
8 处默认值全部移到 param() 之后的解析段,沿用本仓库对 -BackupDir 一直在用的写法。

新增三条可重放的约定,让编码不再是一次性动作:
  .gitattributes 接管行尾(本机 core.autocrlf=true,会把工作区改成 CRLF 制造伪 diff)
  .editorconfig 用 charset = utf-8-bom 锁住 BOM
  tools\Set-SourceEncoding.ps1 是规范化脚本,tools\Invoke-* 之外的任何改动之后都能重放
  test.ps1 是唯一验收入口:Encode + Parse + Unit + Smoke + E2E,在 7 与 5.1 上各跑一遍

test.ps1 的 Encode 层直接检查"必须有 BOM"这条规则。加它的原因很实际:实测本仓库用的
编辑工具在保存时会悄悄去掉 BOM,而丢了 BOM 的文件只在 5.1 上出错、在 7 上完全正常,
没有这条检查就会一直漏过去。

已知未修(下一步处理):5.1 上 Unit 有 6 项、Smoke 有 1 项失败,全部源于测试夹具写
临时文件时没指定编码(5.1 的 Set-Content 默认 ANSI),与产品代码无关。
2026-09-26 22:05:42 +08:00

125 lines
4.3 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<#
.SYNOPSIS
注册 / 移除 BakNRet 的每日备份计划任务。
.DESCRIPTION
任务直接调用 Backup.ps1。脚本自身会写日志并按失败数返回退出码,
因此「上次运行结果」在任务计划程序里是可读的,不需要额外包装。
注册计划任务需要管理员权限(本脚本不自己提权,请从管理员终端运行)。
.EXAMPLE
# 先看将要注册什么(只读,不需要管理员)
.\tools\Register-BackupTask.ps1 -At '21:30' -DryRun
.EXAMPLE
.\tools\Register-BackupTask.ps1 -At '21:30'
.EXAMPLE
.\tools\Register-BackupTask.ps1 -Remove
#>
[CmdletBinding(SupportsShouldProcess = $true)]
param(
[string]$TaskName = 'BakNRet Backup',
# 每日触发时间,HH:mm
[ValidatePattern('^\d{1,2}:\d{2}$')]
[string]$At = '21:30',
# 额外传给 Backup.ps1 的参数,例如 @('-Snapshot')
[string[]]$BackupArgument = @(),
[ValidateSet('S4U', 'Interactive')]
[string]$LogonType = 'S4U',
# 用哪个 PowerShell 宿主执行(默认优先 pwsh.exe)
[string]$PowerShellPath,
[switch]$Remove,
[switch]$DryRun
)
$ErrorActionPreference = 'Stop'
$projectRoot = Split-Path -Parent $PSScriptRoot
$backupScript = Join-Path $projectRoot 'Backup.ps1'
if (-not (Test-Path -LiteralPath $backupScript)) {
Write-Error "找不到 Backup.ps1:$backupScript"
exit 1
}
if (-not $PowerShellPath) {
$pwsh = Get-Command pwsh.exe -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty Source
$PowerShellPath = if ($pwsh) { $pwsh } else { (Get-Command powershell.exe -ErrorAction Stop).Source }
}
$argumentParts = @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', "`"$backupScript`"") + $BackupArgument
$argument = ($argumentParts -join ' ')
Write-Host ''
Write-Host "任务名 : $TaskName"
Write-Host "执行程序 : $PowerShellPath"
Write-Host "参数 : $argument"
Write-Host "触发 : 每天 $At"
Write-Host "登录类型 : $LogonType(S4U = 未登录也运行,但访问不到网络资源)"
Write-Host ''
if ($Remove) {
if ($DryRun) {
Write-Host '[试运行] 将移除计划任务' -ForegroundColor Yellow
exit 0
}
if (-not (Get-Command Unregister-ScheduledTask -ErrorAction SilentlyContinue)) {
Write-Error '当前环境没有计划任务 cmdlet。'
exit 1
}
try {
Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false -ErrorAction Stop
Write-Host "已移除计划任务:$TaskName" -ForegroundColor Green
} catch {
Write-Error "移除失败(多半是权限不足,请用管理员终端):$_"
exit 1
}
exit 0
}
if ($DryRun) {
Write-Host '[试运行] 未实际注册。去掉 -DryRun 并确保在管理员终端里执行即可注册。' -ForegroundColor Yellow
exit 0
}
foreach ($cmdlet in 'Register-ScheduledTask', 'New-ScheduledTaskTrigger', 'New-ScheduledTaskAction', 'New-ScheduledTaskSettingsSet', 'New-ScheduledTaskPrincipal') {
if (-not (Get-Command $cmdlet -ErrorAction SilentlyContinue)) {
Write-Error "当前环境缺少 $cmdlet,无法注册计划任务。"
exit 1
}
}
$action = New-ScheduledTaskAction -Execute $PowerShellPath -Argument $argument -WorkingDirectory $projectRoot
$trigger = New-ScheduledTaskTrigger -Daily -At $At
$settings = New-ScheduledTaskSettingsSet `
-StartWhenAvailable `
-MultipleInstances IgnoreNew `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-ExecutionTimeLimit (New-TimeSpan -Hours 6)
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$principal = New-ScheduledTaskPrincipal -UserId $identity -LogonType $LogonType -RunLevel Highest
try {
Register-ScheduledTask -TaskName $TaskName -Action $action -Trigger $trigger `
-Settings $settings -Principal $principal -Force -ErrorAction Stop | Out-Null
Write-Host "已注册计划任务:$TaskName(每天 $At)" -ForegroundColor Green
Write-Host "查看上次运行结果:Get-ScheduledTaskInfo -TaskName '$TaskName'" -ForegroundColor DarkGray
Write-Host "手动跑一次验证 :Start-ScheduledTask -TaskName '$TaskName'" -ForegroundColor DarkGray
} catch {
Write-Error "注册失败(多半是权限不足,请用管理员终端):$_"
exit 1
}
exit 0