通过


about_Error_Handling

简短说明

介绍 PowerShell 中的错误类型和处理错误的机制。

详细说明

PowerShell 区分三类错误:

  • 非终止错误
  • 语句终止错误
  • 脚本终止错误

了解区别对于编写可靠的脚本和模块至关重要,因为每个类别都有不同的默认行为,并且需要不同的处理技术。

此外,外部(本机)程序通过退出代码报告失败,PowerShell 会单独跟踪其自己的错误系统。

错误的类型

非终止错误

非终止错误报告问题,但不停止管道。 该命令继续处理后续输入对象。 非终止错误由:

  • Write-Error cmdlet
  • $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/catchtrap.

注释

.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 将生成脚本终止错误。 但是,$ErrorActionPreferencethrow设置为SilentlyContinueIgnore. 使用 <a0/a0> 调用高级函数时,该参数将转换为范围本地值,因此它还会禁止在该函数内部。

注释

即使如此 $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
  • 生成 ErrorRecord in$Error
  • 触发catchtrap

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 使用以下机制将非终止错误转换为终止错误:

  1. cmdlet 在内部调用 WriteError() 以发出非终止错误。
  2. 引擎检查命令的有效 ErrorAction 首选项。
  3. 因为首选项是 Stop,引擎会创建包装原始错误记录的一个 ActionPreferenceStopException
  4. 如果被 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 参数),并且可以在值为 SilentlyContinueIgnore时禁止显示语句终止错误。

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 块将运行。

  • 默认值 (无 breakcontinue):显示错误,并在导致错误的语句后继续执行下一个语句。
  • 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 确保在 $? 调用方的范围内正确设置 $falseWrite-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 升级:取决于上下文

另见