PAYATHON 2026

如何正确使用 $PSCmdlet.ThrowTerminatingError() 抛出错误?

数据阿明

如果决定使用 $PSCmdlet.ThrowTerminatingError(),可以直接创建一个包含异常、错误标识、错误类别和目标对象的 ErrorRecord,不必先 throw 再 catch。简单的错误消息可以放进 System.Exception 的构造函数。

使用 [CmdletBinding()] 并不意味着必须替换所有 throw 和 Write-Error。具体如何选择,取决于错误要终止的范围,以及是否需要提供结构化错误信息。

直接构造 ErrorRecord

下面是一个完整的高级函数示例:

function Invoke-Example {
    [CmdletBinding()]
    param()

    $exception = [System.Exception]::new('An error occurred!')

    $errorRecord = [System.Management.Automation.ErrorRecord]::new(
        $exception,
        'ExampleOperationFailed',
        [System.Management.Automation.ErrorCategory]::NotSpecified,
        $null
    )

    $PSCmdlet.ThrowTerminatingError($errorRecord)
}

这里使用 ErrorRecord(Exception, String, ErrorCategory, Object) 构造函数,四个参数依次表示:

  • Exception:包含错误消息的异常对象。只有通用消息时,可以使用 System.Exception。
  • 错误标识:示例中的 ExampleOperationFailed,用于标识这类失败,应尽量保持稳定。
  • ErrorCategory:没有更明确的类别时可以使用 NotSpecified;已知失败原因时应选择相应的类别。
  • 目标对象:发生错误时正在处理的对象;没有相关对象时使用 $null。

ErrorRecord 除了消息,还承载错误类别、目标对象和调用信息,因此需要的信息比一个字符串更多。构造函数及相关属性见 ErrorRecord API 文档。

如果明确知道失败原因,优先选择更具体的异常类型。例如,参数值不合适时可以考虑 System.ArgumentException,操作与当前对象状态不兼容时可以考虑 System.InvalidOperationException。

如何选择 ThrowTerminatingError、throw 和 WriteError

在默认错误偏好设置下,可以按以下目的选择:

目的常用方式主要行为
当前高级函数无法继续执行$PSCmdlet.ThrowTerminatingError($errorRecord)产生语句终止错误,停止当前调用所在的语句或管道
需要让未处理的错误沿调用栈传播throw 'An error occurred!'默认产生脚本终止错误
报告当前项目失败,仍允许继续处理$PSCmdlet.WriteError($errorRecord)默认产生非终止错误

语句终止错误和脚本终止错误都可以被 try/catch 捕获,区别在于未捕获时的影响范围:ThrowTerminatingError() 通常允许调用方在失败语句之后继续执行;throw 默认会沿调用栈传播。错误偏好设置可能改变实际行为,两者并不完全等价。参见 PowerShell 错误处理说明。

在高级函数中使用 $PSCmdlet.WriteError() 报告非终止错误,还能让调用方的 $? 正确反映函数失败。函数内部单独调用 Write-Error 则不具备相同的状态传播效果。参见 自动变量中的 $? 说明。

如果需要报告错误后继续处理,可以将前面示例中的最后一行改为:

$PSCmdlet.WriteError($errorRecord)

调用方使用 -ErrorAction Stop 时,这类非终止错误可以升级为终止错误,因此后续代码是否执行也取决于调用方式。

原来的 try/catch 写法是否有问题

原写法能够工作:throw 产生的错误被捕获后,catch 中的 $_ 是一个 ErrorRecord,可以传给 $PSCmdlet.ThrowTerminatingError()。不过,如果只是为了创建新错误,直接构造 ErrorRecord 更清楚,也便于指定错误标识和类别。

如果要捕获底层操作的失败,可以转交已有的错误记录:

function Get-RequiredItem {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$Path
    )

    try {
        Get-Item -LiteralPath $Path -ErrorAction Stop
    }
    catch {
        $PSCmdlet.ThrowTerminatingError($_)
    }
}

这里的 -ErrorAction Stop 用于让 Get-Item 的非终止错误进入 catch,随后由高级函数报告终止错误。如果需要在 catch 中保留 throw 的重新抛出语义,可以直接使用不带参数的 throw。两种方式应按预期的错误传播范围选择。关于传递已有错误记录的用法,参见 PowerShell 异常处理指南。

备注:内容仅供参考。