在Powershell模块功能中发出错误的正确方法是什么?

时间:2019-10-23 05:33:19

标签: powershell powershell-5.0

我已经阅读了很多有关Powershell错误处理的知识,现在我对在任何给定情况下(错误处理)应该做什么感到很困惑。我正在使用Powershell 5.1(不是核心)。 照这样说: 假设我有一个模块,其功能类似于此模拟:

function Set-ComputerTestConfig {
  [CmdletBinding()]
  param(
    [Parameter(Position=0, Mandatory=$true)]
    [ValidateNotNullOrEmpty()]
    [string] $Name)

begin { ... }
process { 
 # task 1
 # task 2 => results in a failure that prevents further tasks
 # task 3
 # task 4
}
end { ... }

假设对于传递给此函数的每台计算机名称,我有4个任务要完成,但是如果任何一个任务失败,我将无法继续执行其余任务。 我应该如何产生一个错误(最佳实践),使该特定计算机名称的“进程”暂停,但有效地继续处​​理管道?

2 个答案:

答案 0 :(得分:1)

  • 如果要继续处理管道中的输入,则必须发出非终止错误

    >
    • Write-Error写入非终止错误;它写入PowerShell的错误流,而不会在后台生成异常;执行正常继续。

      • 如果.NET方法调用是错误源,例如您的情况,请将其包装在try / catch中,然后在{{1}中调用Write-Error -ErrorRecord $_ }区块:

        • catch
      • 不幸的是,从PowerShell Core 7.0.0-preview.4开始,try { <#task 1 #>; ... } catch { Write-Error -ErrorRecord $_ }仍未完全按预期运行,因为它没有设置自动成功状态变量{{ 1}},并在调用者的上下文中Write-Error。当前唯一的解决方法是确保您的函数/脚本是advanced,并使用$?;通过$false块中的 您可以简单地使用$PSCmdlet.WriteError(),但是从头开始编写自己的错误很麻烦-请参见this GitHub issue

  • 如果要立即停止处理,请使用终止错误

    • catch创建终止错误。

      • 不幸的是,$PSCmdlet.WriteError($_)产生的终止错误类型比二进制cmdlet发出的错误类型更多:不同于(em> statement 声明的终止错误,由(compiled )cmdlet,throw会创建一个 script 终止错误(运行空间终止错误)

        • 也就是说,默认情况下,二进制cmdlet的语句终止错误只会终止手头的语句(管道)并继续执行封闭脚本,而throw则默认终止整个脚本(及其调用方)。
      • 同样,解决方法要求您的脚本/函数是高级脚本,因此您可以调用throw而不是throw,这样可以正确生成一个声明终止错误;与$PSCmdlet.ThrowTerminatingError()一样,您可以简单地从throw块中使用$PSCmdlet.WriteError() ,但是从头开始编写自己的语句终止错误很麻烦。

  • 关于$PSCmdlet.ThrowTerminatingError($_)

    • 这会将所有错误类型转换为 script 终止错误,以及至少 高级函数/脚本-预期会像cmdlet一样工作-应该不要设置

    • 相反,让脚本/函数发出适当类型的错误,并让调用方通过常见的catch参数或{ {1}}变量。

      • 注意事项:如果调用方位于模块外部或其他模块中,则模块中的功能不会看到调用方的首选项变量- this GitHub issue中讨论了这个基本问题。
  • 关于从功能脚本内部通过 / 重新包装传递错误

    • 非终止错误会自动传递

      • 如果需要,可以用$ErrorActionPreference = 'Stop'-ErrorAction禁止显示它们,还可以选择使用$ErrorActionPreference公用参数(与-ErrorAction Ignore组合)收集它们以供以后处理。 / li>
    • 脚本终止的错误会通过整个运行空间以及代码的终止来传递。

    • 声明-终止错误被写入错误流,但是默认情况下,脚本/函数继续继续运行。

      • 使用2>$null来将其转换为 script 终止错误,或者...

      • ...使用-ErrorVariable而不是-ErrorAction SilentlyContinue来将错误作为声明终止。

进一步阅读

答案 1 :(得分:-1)

使用Try ... Catch-确保所有命令都使用-ErrorAction Stop开关或将环境设置为在发生错误时停止,例如$ErrorActionPreference = 'Stop'

function Set-ComputerTestConfig {
  [CmdletBinding()]
  param(
    [Parameter(Position=0, Mandatory=$true)]
    [ValidateNotNullOrEmpty()]


    [string] $Name)

begin { ... }
process {
    Try {
        # task 1
        # task 2 => results in a failure that prevents further tasks
        # task 3
        # task 4
    }
    Catch {
        Write-Host "One of the tasks has failed`nError Message:"
        Write-Host $_
    }
}
end { ... }