在 powershell 模块函数中发出错误的正确方法是什么? [英] What's the right way to emit errors in powershell module functions?
问题描述
我已经阅读了很多关于 powershell 错误处理的内容,现在我对在任何给定情况下应该做什么(错误处理)感到非常困惑.我正在使用 powershell 5.1(不是核心).照这样说:假设我有一个模块,它的函数看起来像这个模拟:
I've read quite a bit on powershell error handling and now I'm quite confused about what I should be doing on any given situation (error handling). I'm working with powershell 5.1 (not core). With that said: Suppose I have a module with a function that would look like this mock:
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 个任务要完成,但如果其中任何一个任务失败,我将无法继续执行剩余的任务.我应该如何产生错误(最佳实践),以便它停止此特定计算机名称的进程",但有效地继续处理管道?
Let's say that for each computer name that I pass to this function, I have 4 tasks to complete, but if any of the tasks fail, I can't continue with the remaining tasks. How should I be producing an error (best practice) such that it halts "process" for this particular computer name but effectively continues to process the pipeline?
推荐答案
如果您想继续处理来自管道的输入,您必须发出一个非终止错误:
If you want to continue processing inputs from the pipeline, you must emit a non-terminating error:
Write-Error
写入非终止错误;它写入 PowerShell 的错误流,而不会在幕后产生异常;执行正常继续.
Write-Error
writes non-terminating errors; it writes to PowerShell's error stream without generating an exception behind the scenes; execution continues normally.
如果 .NET 方法调用是错误源(如您的情况),请将其包装在
try
/catch
中,然后调用Write-
:catch
块中的错误 -ErrorRecord $_
If a .NET method call is the error source, as in your case, wrap it in
try
/catch
, and callWrite-Error -ErrorRecord $_
in thecatch
block:
try { <#task 1 #>;... } catch { Write-Error -ErrorRecord $_ }
不幸的是,从 PowerShell Core 7.0.0-preview.4 开始,
Write-Error
没有完全按预期运行,因为它没有设置自动成功 -状态变量$?
在调用者的上下文中设置为$false
,这是应该的.目前唯一的解决方法是确保您的函数/脚本是 高级 一并使用$PSCmdlet.WriteError()
;从catch
块你可以简单地使用$PSCmdlet.WriteError($_)
,但是从头开始制作你自己的错误很麻烦 - 见 GitHub 问题 #3629.Unfortunately, still as of PowerShell Core 7.0.0-preview.4,
Write-Error
doesn't fully behave as expected, in that it doesn't set the automatic success-status variable,$?
, to$false
in the caller's context, as it should. The only workaround at present is to make sure that your function/script is an 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.如果您希望处理立即停止,请使用终止错误:
If you want processing to stop right away, use a terminating error:
throw
会产生终止错误.不幸的是,
throw
创建了一种比二进制 cmdlet 发出的基本类型的终止错误:不同于 语句-终止由(编译的)cmdlet 发出的错误,throw
创建一个脚本-终止(致命)错误.
throw
creates terminating errors.Unfortunately,
throw
creates a more fundamental kind of terminating error than binary cmdlets emit: unlike the statement-terminating errors emitted by (compiled) cmdlets,throw
creates a script-terminating (fatal) error.
- 也就是说,默认情况下,二进制 cmdlet 的语句终止错误仅终止手头的语句(管道)并继续执行封闭脚本,而
throw
在默认情况下会中止整个脚本(及其来电者). - GitHub 问题 #14819 讨论了这种不对称性.
- That is, by default a binary cmdlet's statement-terminating error only terminates the statement (pipeline) at hand and continues execution of the enclosing script, whereas
throw
by default aborts the entire script (and its callers). - GitHub issue #14819 discusses this asymmetry.
同样,解决方法要求您的脚本/函数是高级,这使您能够调用
$PSCmdlet.ThrowTerminatingError()
而不是throw
,它正确地生成一个语句终止错误;与$PSCmdlet.WriteError()
一样,您可以简单地使用$PSCmdlet.ThrowTerminationError($_)
来自catch
块,但是从头开始编写自己的语句终止错误很麻烦.Again, the workaround requires that your script/function is an advanced one, which enables you to call
$PSCmdlet.ThrowTerminatingError()
instead ofthrow
, which properly generates a statement-terminating error; as with$PSCmdlet.WriteError()
, you can simply use$PSCmdlet.ThrowTerminatingError($_)
from acatch
block, but crafting your own statement-terminating error from scratch is cumbersome.至于
$ErrorActionPreference = 'Stop'
这会将所有错误类型变成脚本-终止错误,以及至少高级函数/脚本-那些应该像 cmdlet 一样运行 - 应该不设置它.
This turns all error types into script-terminating errors, and at least advanced functions / scripts - those expected to act like cmdlets - should not set it.
相反,让您的脚本/函数发出适当类型的错误,并让调用者通过常见的
-ErrorAction
参数控制对它们的响应或通过$ErrorActionPreference
变量.Instead, make your script / function emit the appropriate types of errors and let the caller control the response to them, either via the common
-ErrorAction
parameter or via the$ErrorActionPreference
variable.- 警告:如果调用者在模块外或在不同的模块中,模块中的函数不会看到调用者的偏好变量 -GitHub 问题 #4568 中讨论了这个基本问题.
- Caveat: Functions in modules do not see the caller's preference variables, if the caller is outside a module or in a different module - this fundamental problem is discussed in GitHub issue #4568.
至于通过传递错误/从函数脚本内部重新打包:
As for passing errors through / repackaging them from inside your function script:
非终止性错误自动传递.
- 如果需要,您可以使用
-ErrorAction Ignore
或2>$null
抑制它们,也可以选择收集它们以供以后使用-ErrorVariable处理code> 公共参数(与
-ErrorAction SilentlyContinue
结合).
- If needed, you can suppress them with
-ErrorAction Ignore
or2>$null
and optionally also collect them for later processing with the-ErrorVariable
common parameter (combine with-ErrorAction SilentlyContinue
).
脚本终止错误,整个调用堆栈默认终止,代码终止.
Script-terminating errors are passed through in the sense that the entire call stack is terminated by default, along with your code.
语句-终止错误会写入错误流,但默认情况下您的脚本/函数继续运行.
Statement-terminating errors are written to the error stream, but by default your script / function continues to run.
使用
try { ... } catch { throw }
来代替将它们变成 script-终止错误,或者 ...
Use
try { ... } catch { throw }
to instead turn them into script-terminating errors, or ...
... 使用
$PSCmdlet.ThrowTerminatingError($_)
而不是throw
将错误作为 statement-终止一个.... use
$PSCmdlet.ThrowTerminatingError($_)
instead ofthrow
to relay the error as a statement-terminating one.进一步阅读:
关于何时发出终止与非终止错误的指南在这个答案中.
PowerShell 错误处理的全面概述位于 GitHub 文档问题 #1583.
A comprehensive overview of PowerShell's error handling is in GitHub docs issue #1583.
这篇关于在 powershell 模块函数中发出错误的正确方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!