简短说明
介绍 PowerShell 中的错误类型和处理错误的机制。
详细说明
PowerShell 区分三类错误:
- 非终止错误
- 语句终止错误
- 脚本终止错误
了解区别对于编写可靠的脚本和模块至关重要,因为每个类别都有不同的默认行为,并且需要不同的处理技术。
此外,外部(本机)程序通过退出代码报告失败,PowerShell 会单独跟踪其自己的错误系统。
错误的类型
非终止错误
非终止错误报告问题,但不停止管道。 该命令继续处理后续输入对象。 非终止错误由:
-
Write-Errorcmdlet -
$PSCmdlet.WriteError()高级函数中的方法 - 在单个输入对象上遇到可恢复故障的 Cmdlet
默认情况下,PowerShell 会显示错误消息并继续执行。
# Non-terminating error: the pipeline continues after the failure
'file1.txt', 'noSuchFile.txt', 'file3.txt' | ForEach-Object {
Get-Content $_ -ErrorAction Continue
}
在此示例中, Get-Content 报告一个非终止错误 noSuchFile.txt ,然后继续处理 file3.txt。
非终止错误不会触发或trap默认情况下触发catch。
语句终止错误
语句终止错误会停止当前语句(管道)的运行,但在脚本中的下一个语句中继续执行。 语句终止错误由以下方法生成:
-
$PSCmdlet.ThrowTerminatingError()高级函数和已编译 cmdlet 中的方法 - 引擎错误,例如
CommandNotFoundException(调用不存在的命令)和ParameterBindingException(参数参数无效) - 引发异常的 .NET 方法调用,例如
[int]::Parse('abc')
# Statement-terminating error: Get-Item fails, but the next statement runs
Get-Item -Path 'C:\NoSuchFile.txt'
Write-Output 'This still runs'
语句终止错误可由 try/catch 和 trap.
注释
.ThrowTerminatingError() 不查阅 -ErrorAction 参数(输入调试器的值除外 Break )。 但是,$ErrorActionPreference通过引擎的语句级处理程序,确实适用于语句终止错误。 例如, $ErrorActionPreference = 'SilentlyContinue' 可以取消语句终止错误,以便脚本在下一个语句中继续。 参数 -ErrorAction 无法执行此操作。 有关详细信息,请参阅 $ErrorActionPreference不对称。
脚本终止错误
脚本终止错误展开整个调用堆栈。 除非阻止或trap语句捕获try/catch错误,否则执行将完全停止。 脚本终止错误由以下方法生成:
-
throw关键字 - 分析错误(阻止编译脚本的语法错误)
- 非终止错误由
-ErrorAction Stop$ErrorActionPreference = 'Stop'非高级上下文或非高级上下文升级。 有关详细信息,请参阅 升级的工作原理。 - 某些关键引擎故障
# Script-terminating error: throw unwinds the call stack
function Test-Throw {
throw 'Critical failure'
Write-Output 'This never runs'
}
Test-Throw
Write-Output 'This never runs either (unless caught)'
默认情况下,关键字 throw 将生成脚本终止错误。 但是,$ErrorActionPreference当throw设置为SilentlyContinue或 Ignore. 使用 <
注释
即使如此 $ErrorActionPreference = 'Ignore', throw 该控件仍会记录一个条目 $Error。 该值 Ignore 仅阻止 $Error 记录 非终止 错误。
重要
术语 语句终止 和 脚本终止 描述影响范围,而不是错误的严重性。 语句终止错误停止一个语句。 脚本终止错误会停止整个脚本及其调用方。 两者都可以被 try/catch抓住。
外部程序错误
外部(本机)程序不直接参与 PowerShell 的错误系统。 它们通过 PowerShell 存储在自动变量中的 $LASTEXITCODE 非零退出代码报告失败。
git clone https://example.com/nonexistent.git 2>$null
if ($LASTEXITCODE -ne 0) {
Write-Error "git failed with exit code $LASTEXITCODE"
}
默认情况下,本机程序的非零退出代码:
- 将 设置为
$?$false -
不生成
ErrorRecordin$Error -
不触发
catch或trap
PowerShell 7.3 添加了实验性首选项变量,该变量 $PSNativeCommandUseErrorActionPreference成为 7.4 中的稳定功能。 将此变量 $true设置为时,它会导致非零退出代码发出 非终止错误 ,其消息指出了特定的退出代码(a NativeCommandExitException)。 此错误遵循$ErrorActionPreference,因此将其设置为将Stop错误提升为脚本终止错误,该错误可通过该错误捕获/trycatch。
错误状态变量
PowerShell 维护多个反映当前错误状态的自动变量。
$?
包含 $true 最后一个操作是否成功,以及 $false 它是否生成了任何错误(非终止或终止)。 对于本机命令,$?基于退出代码设置:$true对于退出代码,$false否则为退出代码0。
Get-Item -Path 'C:\NoSuchFile.txt' 2>$null
$? # False
$Error
一个 ArrayList 存储最新错误记录,索引处 0是最新的错误。 列表最多保留 $MaximumErrorCount 条目(默认值 256)。
所有终止错误都会添加到 $Error。 对于终止错误,取消显示,Ignore但仍会记录错误。$Error 除非-ErrorAction Ignore在非终止错误上使用所有非终止,否则会阻止$Error显示和录制。
$LASTEXITCODE
包含运行的最后一个本机程序的退出代码。 通常的值 0 表示成功。 任何非零值都表示失败。 此变量不受 PowerShell cmdlet 错误的影响。
控制错误行为
-ErrorAction通用参数
单个命令的 -ErrorAction 通用参数重写 $ErrorActionPreference 。 它控制 PowerShell 如何响应来自该命令 的非终止 错误。
| 价值 | 行为 |
|---|---|
Continue |
显示错误并继续(默认值) |
SilentlyContinue |
禁止显示、添加到 $Error、继续 |
Ignore |
禁止显示且不添加到 $Error |
Stop |
升级到 终止错误(请参阅 升级的工作原理) |
Inquire |
提示用户做出决策 |
Break |
输入调试器 |
-ErrorAction 不会更改由 $PSCmdlet.ThrowTerminatingError(). 生成的错误的行为。 无论调用方偏好如何,这些错误始终都是语句终止。
变量$ErrorActionPreference
$ErrorActionPreference首选项变量适用于当前作用域和子范围中的所有命令。 它接受与 . 相同的值 -ErrorAction。
$ErrorActionPreference = 'Stop'
# All non-terminating errors in this scope now become terminating
Write-Error 'This now throws' # Generates ActionPreferenceStopException
在命令上指定时 -ErrorAction ,该命令优先于 $ErrorActionPreference 该命令。
升级的工作原理
当或$ErrorActionPreference = 'Stop'生效时-ErrorAction Stop,PowerShell 使用以下机制将非终止错误转换为终止错误:
- cmdlet 在内部调用
WriteError()以发出非终止错误。 - 引擎检查命令的有效
ErrorAction首选项。 - 因为首选项是
Stop,引擎会创建包装原始错误记录的一个ActionPreferenceStopException。 - 如果被
catch捕获,可通过访问$_.Exception.ErrorRecord原始错误信息。
升级错误的范围取决于上下文:
- 在 非高级 脚本、函数或脚本块中,设置
$ErrorActionPreference = 'Stop'会升级到 脚本终止 错误。 该错误将传播调用堆栈。 - 在 高级 函数和脚本块(具有
[CmdletBinding()])中,错误仍然是 语句终止。 调用后,下一个语句将继续执行。 -
-ErrorAction Stop传递给高级函数的效果与在其中设置$ErrorActionPreference = 'Stop'的效果相同,因为-ErrorAction会转换为范围本地$ErrorActionPreference值。
升级示例
非高级:脚本终止('after'未打印)
& { param() $ErrorActionPreference = 'Stop' Get-Item 'NoSuchPath' } 2>$null 'after'ADVANCED: statement-terminating ('after' DOES print)
& { [CmdletBinding()] param() $ErrorActionPreference = 'Stop'; Get-Item 'NoSuchPath' } 2>$null 'after'没有
-ErrorAction Stop:非终止,catch 不会运行try { Write-Error 'This is non-terminating' Write-Output 'Execution continues' } catch { Write-Output "Caught: $_" # Not reached }使用
-ErrorAction Stop:升级为终止try { Write-Error 'This becomes terminating' -ErrorAction Stop } catch { Write-Output "Caught: $_" # Reached }
升级的错误可以通过原始异常类型捕获。 引擎解 ActionPreferenceStopException 包用于查找基础异常:
try {
Get-Item -Path 'C:\NoSuchFile.txt' -ErrorAction Stop
} catch [System.Management.Automation.ItemNotFoundException] {
Write-Output "File not found: $($_.Exception.Message)"
}
$ErrorActionPreference不对称
参数 -ErrorAction 和 $ErrorActionPreference 变量的行为方式与终止错误不同。 请务必了解此不对称性:
-ErrorAction仅影响 非终止 错误。 当 cmdlet 调用$PSCmdlet.ThrowTerminatingError()时,参数-ErrorAction将被忽略(但输入调试器除外Break)。 始终引发错误。$ErrorActionPreference影响非终止错误和语句终止错误。 引擎的语句级错误处理程序读取$ErrorActionPreference(而不是-ErrorAction参数),并且可以在值为SilentlyContinue或Ignore时禁止显示语句终止错误。
function Test-Asymmetry {
[CmdletBinding()]
param()
$er = [System.Management.Automation.ErrorRecord]::new(
[System.InvalidOperationException]::new('test error'),
'TestError',
[System.Management.Automation.ErrorCategory]::InvalidOperation,
$null
)
$PSCmdlet.ThrowTerminatingError($er)
}
# -ErrorAction SilentlyContinue does NOT suppress the error:
Test-Asymmetry -ErrorAction SilentlyContinue # Error is still thrown
# $ErrorActionPreference DOES suppress the error:
$ErrorActionPreference = 'SilentlyContinue'
Test-Asymmetry # Error is silently suppressed, script continues
$ErrorActionPreference = 'Continue'
重要
$ErrorActionPreference 无法禁止显示已 SuppressPromptInInterpreter 设置为 true.. 无论首选项变量如何,这些变量始终传播。 此类错误的示例包括:
-
ActionPreferenceStopException从-ErrorAction Stop升级 - PowerShell 类方法中的错误
PipelineStoppedException
处理错误
try/catch/finally
用于 try/catch/finally 处理语句终止和脚本终止错误。 当块内 try 发生错误时,PowerShell 会搜索匹配 catch 的块。 块 finally 始终运行,无论是否发生错误。
try {
$result = Get-Content -Path 'data.txt' -ErrorAction Stop
}
catch [System.Management.Automation.ItemNotFoundException] {
Write-Warning 'Data file not found, using defaults.'
$result = 'default'
}
catch {
Write-Warning "Unexpected error: $_"
}
finally {
Write-Verbose 'Cleanup complete.' -Verbose
}
try在块内,引擎设置一个内部标志,该标志会导致非-ErrorAction Stop终止错误升级或$ErrorActionPreference = 'Stop'传播到catch块。 这是设计的行为,而不是特殊情况。
有关完整的语法详细信息,请参阅 about_Try_Catch_Finally。
trap
该 trap 语句在范围级别处理终止错误。 当在封闭范围中的任何位置发生错误时,该 trap 块将运行。
-
默认值 (无
break或continue):显示错误,并在导致错误的语句后继续执行下一个语句。 -
continue在陷阱中:取消错误消息并在下一个语句中恢复。 -
break中的陷阱:错误传播到父范围。
trap [System.Management.Automation.CommandNotFoundException] {
Write-Warning "Command not found: $($_.TargetObject)"
continue
}
NonsenseCommand # Trap fires, execution continues
Write-Output 'This runs because the trap used continue'
有关完整的语法详细信息,请参阅 about_Trap。
报告函数和脚本中的错误
编写函数和脚本时,请选择与故障严重性匹配的错误报告机制。
非终止 - 使用 Write-Error
当函数可以继续处理其他输入时使用 Write-Error 。 这适用于处理多个对象的管道函数,并在单个项上遇到故障。
function Test-Path-Safe {
[CmdletBinding()]
param([Parameter(ValueFromPipeline)][string]$Path)
process {
if (-not (Test-Path $Path)) {
Write-Error "Path not found: $Path"
return
}
$Path
}
}
注释
在高级函数(具有 [CmdletBinding()])中使用 $PSCmdlet.WriteError() ,而不是 Write-Error 确保在 $? 调用方的范围内正确设置 $false 。
Write-Error cmdlet 并不总是正确设置$?。
语句终止 - 使用 $PSCmdlet.ThrowTerminatingError()
$PSCmdlet.ThrowTerminatingError()当函数根本无法继续,但调用方应决定如何处理失败时使用。 这是高级函数中的推荐方法。
function Get-Config {
[CmdletBinding()]
param([string]$Path)
if (-not (Test-Path $Path)) {
$er = [System.Management.Automation.ErrorRecord]::new(
[System.IO.FileNotFoundException]::new("Config file not found: $Path"),
'ConfigNotFound',
[System.Management.Automation.ErrorCategory]::ObjectNotFound,
$Path
)
$PSCmdlet.ThrowTerminatingError($er)
}
Get-Content $Path | ConvertFrom-Json
}
错误离开函数后,调用方默认将其视为非终止错误。 调用方可以使用 -ErrorAction Stop.
脚本终止 - 使用 throw
throw如果无法恢复,并且整个脚本应停止,请使用。
$config = Get-Content 'config.json' -ErrorAction SilentlyContinue |
ConvertFrom-Json
if (-not $config) {
throw 'Cannot proceed without a valid configuration file.'
}
要使用的机制
- 处理多个输入时,某些输入可能会失败,请使用
Write-Error或$PSCmdlet.WriteError()。 - 如果函数无法继续,请使用
$PSCmdlet.ThrowTerminatingError()并让调用方决定如何处理它。 - 如果整个脚本必须立即停止,请使用
throw。
错误类型的摘要
下表汇总了 PowerShell 中不同错误类型的属性和行为。
非终止错误
非终止错误 可由 Write-Error 或 $PSCmdlet.WriteError().
| Attribute | 说明 |
|---|---|
| 影响范围 | 管道继续 |
被抓住 catch |
否(除非升级) |
被抓住 trap |
否(除非升级) |
已添加到 $Error |
是(除非 Ignore) |
将 设置为$?$false |
是的 |
受 -ErrorAction |
是的 |
受 $ErrorActionPreference |
是的 |
语句终止错误
语句终止错误可以通过引擎错误、.NET 方法异常或在-ErrorAction Stop高级上下文中生成ThrowTerminatingError()。
| Attribute | 说明 |
|---|---|
| 影响范围 | 当前语句停止;脚本继续 |
被抓住 catch |
是的 |
被抓住 trap |
是的 |
已添加到 $Error |
是的 |
将 设置为$?$false |
是的 |
受 -ErrorAction |
否(Break 仅) |
受 $ErrorActionPreference |
是(可以抑制) |
脚本终止错误
脚本终止错误可以通过分析错误或在-ErrorAction Stop非高级上下文中生成throw。
| Attribute | 说明 |
|---|---|
| 影响范围 | 调用堆栈展开 |
被抓住 catch |
是的 |
被抓住 trap |
是的 |
已添加到 $Error |
是的 |
将 设置为$?$false |
是的 |
受 -ErrorAction |
否 |
受 $ErrorActionPreference |
throw:是(可以抑制) |
受 $ErrorActionPreference |
升级:取决于上下文 |