Windows Server 2025 / 处理 PowerShell 错误并记录执行日志
将 try、catch、finally 与明确的错误处理方式结合,并为每次 PowerShell 执行单独保存日志,可以追踪失败原因和部分完成的结果。命令已经结束,并不能单独证明整项管理任务已经成功。
让失败可被识别
本练习读取文件并写入日志,不修改服务器角色、服务或网络配置。实际的安装或账户管理脚本还需要记录:某一步失败之前,哪些修改已经生效。进入 catch 不会自动撤销之前的操作。
有些错误允许 PowerShell 继续执行,默认不会触发 catch。因此,仅把命令放进 try 并不足够;-ErrorAction Stop 可以将支持的非终止性错误提升为可捕获的终止性错误。Microsoft 的 PowerShell 错误处理说明介绍了这些区别。
准备输入文件与完整脚本
先创建 C:\LabScripts,再将 C:\LabScripts\input.txt 保存为只包含一行原样文本 lab ready 的纯文本文件。将下面的完整代码保存为 C:\LabScripts\Read-LabFile.ps1,并检查 .txt 和 .ps1 扩展名。使用 UTF-8;如果在脚本中加入中文等非 ASCII 字符,应选择带字节顺序标记(BOM,byte order mark)的 UTF-8,参阅字符编码说明。使用 Windows PowerShell 5.1 运行,并确保有权读取输入文件以及创建或写入 C:\LabLogs。脚本以 JSON(JavaScript Object Notation,JavaScript 对象表示法)保存结构化结果,文件名包含时间戳和短标识符。
param(
[Parameter(Mandatory=$true)]
[string]$InputPath
)
$logDirectory = 'C:\LabLogs'
New-Item -ItemType Directory -Path $logDirectory -Force -ErrorAction Stop | Out-Null
$runLabel = (Get-Date -Format 'yyyyMMdd-HHmmss') + '-' + ([guid]::NewGuid().ToString('N').Substring(0,6))
$logPath = Join-Path $logDirectory ($runLabel + '.json')
$result = [ordered]@{
StartedAt = (Get-Date).ToString('o')
ComputerName = $env:COMPUTERNAME
InputPath = $InputPath
Success = $false
LineCount = $null
ErrorId = $null
ErrorMessage = $null
}
$exitStatus = 0
try {
$lines = @(Get-Content -LiteralPath $InputPath -ErrorAction Stop)
$result.LineCount = $lines.Count
$result.Success = $true
}
catch {
$result.ErrorId = $_.FullyQualifiedErrorId
$result.ErrorMessage = $_.Exception.Message
$exitStatus = 1
Write-Warning $result.ErrorMessage
}
finally {
$result.FinishedAt = (Get-Date).ToString('o')
$result | ConvertTo-Json | Set-Content -LiteralPath $logPath -Encoding UTF8 -ErrorAction Stop
}
Write-Output $logPath
exit $exitStatus
在相同条件下比较成功与失败
请在 PowerShell 控制台中执行这些命令。每次调用 powershell.exe 都会启动子进程,exit 结束的是该子进程,原控制台仍可继续使用。先调用现有文件,立即查看紧接着的 $LASTEXITCODE;再对 missing-input.txt 重复这一对命令,并确认该文件确实不存在。无需删除有效输入或修改系统文件来制造失败:
powershell.exe -NoProfile -File 'C:\LabScripts\Read-LabFile.ps1' -InputPath 'C:\LabScripts\input.txt'
$LASTEXITCODE
powershell.exe -NoProfile -File 'C:\LabScripts\Read-LabFile.ps1' -InputPath 'C:\LabScripts\missing-input.txt'
$LASTEXITCODE
对于一行有效输入,第一次执行应返回退出代码 0,并生成 Success=true、LineCount=1 且无错误详情的 JSON。文件不存在时应返回 1,JSON 中 Success=false、LineCount=null,同时 ErrorId 和 ErrorMessage 有值;错误消息可能随系统语言变化。用文本编辑器打开每次执行实际输出路径对应的 JSON,核对 InputPath、ComputerName、StartedAt 和 FinishedAt。比较这两份新日志,不要误用文件夹里的旧记录。
如果执行策略阻止脚本运行,请遵守组织的执行与签名要求,不要为了本例降低全局设置。
理解各个错误处理区块
| 区块 | 本脚本中的作用 |
|---|---|
| try | 读取文件并记录行数 |
| catch | 记录捕获到的错误标识与消息,设置失败退出代码 |
| finally | 无论读取成功还是失败,都尝试保存本次结果 |
Microsoft 的 try、catch、finally 参考文档说明了终止性错误的处理。本脚本在 try 之前创建 C:\LabLogs:如果准备阶段失败,后面的 catch 不会捕获这个错误。finally 会尝试保存结果,但不能保证有可用空间或写入权限。Success 表示读取是否成功,还需要验证新的 JSON 文件是否有效生成。日志写入也使用 -ErrorAction Stop;没有日志并不能证明任务已无误完成。
外部程序有自己的退出代码。在 Windows PowerShell 5.1 中,非零退出代码不会自动触发 catch。应在程序结束后立即检查 $LASTEXITCODE,必要时用 throw 产生终止性错误。后续外部程序可能覆盖该值;单独使用 -ErrorAction Stop 不会把外部退出代码转换为异常。
为运维脚本定义安全重试条件
- 记录服务器、目标、开始时间和结束时间。
- 先查询现有状态,避免重复创建或意外覆盖。
- 分别记录已成功和已失败的步骤。
- 不要将密码、令牌或不必要的用户文件内容写入日志。
- 如果实现回滚,即撤销已执行的修改,应明确支持的范围及恢复失败的情况。
重新使用 input.txt 的有效路径再运行一次,确认新的退出代码为 0,新的 JSON 中 Success=true、LineCount=1。先保留必要的检查记录,再仅清理已经准确识别的本练习临时文件。给所有命令添加 SilentlyContinue 只会隐藏错误,不能替代错误处理。




