powershell的try-catch仅捕获终止错误,非终止错误需用-erroraction stop、$erroractionpreference="stop"或throw手动转为终止错误才能触发catch;finally确保资源清理。
powershell 的 try-catch 不是万能兜底开关,它只捕获终止错误(terminating errors)。很多命令默认抛出的是非终止错误(non-terminating errors),比如 get-item 找不到文件、remove-item 删除不存在的路径——这类错误不会自动触发 catch,脚本会继续执行。想让 try-catch 真正生效,必须先让错误“变终止”。
明确哪些错误会被 try-catch 捕获
只有满足以下任一条件的错误才会进入 catch 块:
- 命令本身设计为抛出终止错误(如 `throw "手动异常"`、`Write-Error -ErrorAction Stop`)
- 显式用 -ErrorAction Stop 强制将非终止错误升级为终止错误
- 调用 cmdlet 时发生严重故障(如权限拒绝、连接失败且无重试逻辑)
常见误区:直接写 try { Get-Item "C:\Missing.txt" } catch { Write-Host "抓到了" } —— 这通常不会进入 catch,因为 Get-Item 默认报的是非终止错误。
让 try-catch 实际起作用的三种常用方式
1. 对单个命令加 -ErrorAction Stop
这是最轻量、最推荐的做法,精准控制哪条命令需要严格错误处理:
try {
$file = Get-Item "C:\config.json" -ErrorAction Stop
$data = Get-Content $file.FullName | ConvertFrom-Json -ErrorAction Stop
} catch [System.Management.Automation.ItemNotFoundException] {
Write-Warning "配置文件不存在"
} catch [System.Management.Automation.RuntimeException] {
Write-Warning "JSON 解析失败"
} catch {
Write-Warning "其他错误:$($_.Exception.Message)"
}
2. 临时修改 $ErrorActionPreference
适合一段连续操作都需强错误控制(如初始化多个资源):
$oldPref = $ErrorActionPreference
$ErrorActionPreference = "Stop"
try {
$svc = Get-Service "Spooler" -ErrorAction Continue # 此处 -ErrorAction 优先级更高
$log = Get-WinEvent -LogName Application -MaxEvents 10
} finally {
$ErrorActionPreference = $oldPref # 务必恢复,避免影响后续代码
}
3. 手动 throw 自定义终止错误
当逻辑检查失败但命令本身没报错时(如验证返回值为空):
try {
$result = Invoke-RestMethod "https://api.example.com/status"
if (-not $result.ok) {
throw "API 返回异常状态:$($result.status)"
}
} catch {
Write-Error "请求失败:$($_.Exception.Message)"
}
合理使用 catch 类型匹配提升精度
PowerShell 支持按异常类型捕获,比泛用 catch {} 更安全可靠:
-
catch [System.IO.FileNotFoundException]—— 文件未找到 -
catch [Microsoft.PowerShell.Commands.WriteErrorException]—— 来自 Write-Error 的终止错误 -
catch [System.Net.WebException]—— 网络请求失败 -
catch [System.UnauthorizedAccessException]—— 权限不足
注意:类型名可通过 $_.Exception.GetType().FullName 查看;多个类型可用逗号分隔:catch [A],[B] { ... }
finally 是清理资源的可靠位置
无论是否出错、是否被捕获,finally 块总会执行,适合释放句柄、关闭连接、还原设置:
try {
$stream = [System.IO.File]::OpenWrite("output.bin")
$stream.Write($data, 0, $data.Length)
} catch {
Write-Error "写入失败:$($_.Exception.Message)"
} finally {
if ($stream) { $stream.Close() } # 确保流被关闭
}
不复杂但容易忽略:没写 finally 就可能造成资源泄漏,尤其在循环或长期运行脚本中。










