在 powershell 模块函数中发出错误的正确方法是什么?
What's the right way to emit errors in powershell module functions?
我已经阅读了很多关于 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 个任务要完成,但如果其中任何一个任务失败,我将无法继续执行剩余的任务。
我应该如何产生错误(最佳实践)以使其针对此特定计算机名称停止 "process" 但有效地继续处理管道?
使用 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 { ... }
如果你想继续处理来自管道的输入,你必须发出一个非终止 错误:
Write-Error
写入非终止错误;它写入 PowerShell 的错误流,而不会在后台生成异常;执行正常继续。
如果 .NET 方法调用是错误源,如您的情况,请将其包装在 try
/ catch
中,然后在catch
块:
try { <#task 1 #>; ... } catch { Write-Error -ErrorRecord $_ }
不幸的是,从 PowerShell Core 7.0.0-preview.4 开始,Write-Error
并未完全按预期运行,因为它没有设置自动成功状态变量 $?
到调用者上下文中的 $false
,这是应该的。目前唯一的解决方法是确保您的 function/script 是 advanced one and to use $PSCmdlet.WriteError()
; from a catch
block you can simply use $PSCmdlet.WriteError($_)
, but crafting your own error from scratch is cumbersome - see GitHub issue #3629.
如果您希望处理立即停止,请使用终止错误:
throw
产生终止错误。
不幸的是,throw
产生了比二进制 cmdlet 发出的 更基本的 类型的终止错误:与 statement-terminating errors emitted by (compiled) cmdlets, throw
creates a script-terminating (fatal ) 错误.
- 也就是说,默认情况下,二进制 cmdlet 的语句终止错误只会终止手头的语句(管道)并继续执行封闭脚本,而
throw
默认情况下会中止整个脚本(及其调用者) ).
- GitHub issue #14819 讨论了这种不对称性。
同样,变通方法要求您的 script/function 是 高级 ,这使您可以调用 $PSCmdlet.ThrowTerminatingError()
而不是 throw
,它会正确地生成一个 语句 终止错误;与 $PSCmdlet.WriteError()
一样,您可以简单地使用 catch
块中的 $PSCmdlet.ThrowTerminatingError($_)
,但是从头开始制作您自己的语句终止错误很麻烦。
至于$ErrorActionPreference = 'Stop'
这会将所有错误类型变成脚本-终止错误,并且至少高级 功能/脚本 - 那些预期像 cmdlet 一样工作的 - 应该 不 设置它 .
相反,让您的脚本/函数发出适当类型的错误,并让 调用者 控制对它们的响应,或者通过常见的 -ErrorAction
参数或通过 $ErrorActionPreference
变量。
- 警告:模块中的函数不查看调用者的偏好变量,如果调用者在模块外部或不同模块中 - 这个基本问题在 GitHub issue #4568.
中讨论
至于通过传递错误/重新打包它们来自您的函数脚本:
非终止错误自动通过。
- 如果需要,您可以使用
-ErrorAction Ignore
或 2>$null
抑制它们,也可以选择收集它们以供以后使用 -ErrorVariable
公共参数处理(与 -ErrorAction SilentlyContinue
结合) .
脚本 - 终止错误被传递,因为整个调用堆栈与您的代码一起默认终止。
语句-终止错误被写入错误流,但默认情况下你的脚本/函数继续到运行.
使用try { ... } catch { throw }
将它们变成脚本-终止错误,或...
... 使用 $PSCmdlet.ThrowTerminatingError($_)
而不是 throw
将错误中继为 语句 -终止语句。
进一步阅读:
有关何时发出终止错误与非终止错误的指南在 this answer.
中
- 中全面概述了 PowerShell 的错误处理
我已经阅读了很多关于 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 个任务要完成,但如果其中任何一个任务失败,我将无法继续执行剩余的任务。 我应该如何产生错误(最佳实践)以使其针对此特定计算机名称停止 "process" 但有效地继续处理管道?
使用 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 { ... }
如果你想继续处理来自管道的输入,你必须发出一个非终止 错误:
Write-Error
写入非终止错误;它写入 PowerShell 的错误流,而不会在后台生成异常;执行正常继续。如果 .NET 方法调用是错误源,如您的情况,请将其包装在
try
/catch
中,然后在catch
块:try { <#task 1 #>; ... } catch { Write-Error -ErrorRecord $_ }
不幸的是,从 PowerShell Core 7.0.0-preview.4 开始,
Write-Error
并未完全按预期运行,因为它没有设置自动成功状态变量$?
到调用者上下文中的$false
,这是应该的。目前唯一的解决方法是确保您的 function/script 是 advanced one and to use$PSCmdlet.WriteError()
; from acatch
block you can simply use$PSCmdlet.WriteError($_)
, but crafting your own error from scratch is cumbersome - see GitHub issue #3629.
如果您希望处理立即停止,请使用终止错误:
throw
产生终止错误。不幸的是,
throw
产生了比二进制 cmdlet 发出的 更基本的 类型的终止错误:与 statement-terminating errors emitted by (compiled) cmdlets,throw
creates a script-terminating (fatal ) 错误.- 也就是说,默认情况下,二进制 cmdlet 的语句终止错误只会终止手头的语句(管道)并继续执行封闭脚本,而
throw
默认情况下会中止整个脚本(及其调用者) ). - GitHub issue #14819 讨论了这种不对称性。
- 也就是说,默认情况下,二进制 cmdlet 的语句终止错误只会终止手头的语句(管道)并继续执行封闭脚本,而
同样,变通方法要求您的 script/function 是 高级 ,这使您可以调用
$PSCmdlet.ThrowTerminatingError()
而不是throw
,它会正确地生成一个 语句 终止错误;与$PSCmdlet.WriteError()
一样,您可以简单地使用catch
块中的$PSCmdlet.ThrowTerminatingError($_)
,但是从头开始制作您自己的语句终止错误很麻烦。
至于
$ErrorActionPreference = 'Stop'
这会将所有错误类型变成脚本-终止错误,并且至少高级 功能/脚本 - 那些预期像 cmdlet 一样工作的 - 应该 不 设置它 .
相反,让您的脚本/函数发出适当类型的错误,并让 调用者 控制对它们的响应,或者通过常见的
-ErrorAction
参数或通过$ErrorActionPreference
变量。- 警告:模块中的函数不查看调用者的偏好变量,如果调用者在模块外部或不同模块中 - 这个基本问题在 GitHub issue #4568. 中讨论
至于通过传递错误/重新打包它们来自您的函数脚本:
非终止错误自动通过。
- 如果需要,您可以使用
-ErrorAction Ignore
或2>$null
抑制它们,也可以选择收集它们以供以后使用-ErrorVariable
公共参数处理(与-ErrorAction SilentlyContinue
结合) .
- 如果需要,您可以使用
脚本 - 终止错误被传递,因为整个调用堆栈与您的代码一起默认终止。
语句-终止错误被写入错误流,但默认情况下你的脚本/函数继续到运行.
使用
try { ... } catch { throw }
将它们变成脚本-终止错误,或...... 使用
$PSCmdlet.ThrowTerminatingError($_)
而不是throw
将错误中继为 语句 -终止语句。
进一步阅读:
有关何时发出终止错误与非终止错误的指南在 this answer.
中- 中全面概述了 PowerShell 的错误处理