powershell - 在 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 个任务要完成,但如果任何任务失败,我将无法继续执行剩余的任务。我应该如何产生错误(最佳实践),以便它停止此特定计算机名称的“进程”但有效地继续处理管道?
解决方案
如果要继续处理来自管道的输入,则必须发出非终止错误:
Write-Error
写入非终止错误;它写入 PowerShell 的错误流,而不会在后台生成异常;继续正常执行。如果 .NET 方法调用是错误源,如您的情况,请将其包装在
try
/中,然后在块中catch
调用:Write-Error -ErrorRecord $_
catch
try { <#task 1 #>; ... } catch { Write-Error -ErrorRecord $_ }
不幸的是,仍然从 PowerShell Core 7.0.0-preview.4 开始,
Write-Error
它的行为并没有完全符合预期,因为它没有在调用者的上下文中将自动成功状态变量 , 设置为应有的$?
值。$false
目前唯一的解决方法是确保您的函数/脚本是高级的并使用$PSCmdlet.WriteError()
; 从一个catch
你可以简单地使用的块$PSCmdlet.WriteError($_)
开始,但是从头开始制作你自己的错误很麻烦 - 请参阅GitHub 问题 #3629。
如果您想立即停止处理,请使用终止错误:
throw
产生终止错误。不幸的是,它会产生一种比二进制 cmdlet 发出
throw
的更基本的终止错误:与(编译的)cmdlet 发出的语句终止错误不同,throw
它会产生脚本终止(致命)错误。- 也就是说,默认情况下,二进制 cmdlet 的语句终止错误只会终止手头的语句(管道)并继续执行封闭脚本,而
throw
默认情况下会中止整个脚本(及其调用者)。 - GitHub 问题 #14819讨论了这种不对称性。
- 也就是说,默认情况下,二进制 cmdlet 的语句终止错误只会终止手头的语句(管道)并继续执行封闭脚本,而
同样,解决方法要求您的脚本/函数是高级脚本/函数,它使您能够调用
$PSCmdlet.ThrowTerminatingError()
而不是throw
正确地生成语句终止错误;与 一样$PSCmdlet.WriteError()
,您可以简单地使用$PSCmdlet.ThrowTerminatingError($_)
from acatch
block,但是从头开始制作自己的语句终止错误很麻烦。
至于
$ErrorActionPreference = 'Stop'
这会将所有错误类型转换为脚本终止错误,并且至少高级函数/脚本(预期行为类似于 cmdlet)不应设置它。
相反,让您的脚本/函数发出适当类型的错误,并让调用者通过公共
-ErrorAction
参数或$ErrorActionPreference
变量控制对它们的响应。- 警告:模块中的函数看不到调用者的偏好变量,如果调用者在模块之外或在不同的模块中 - 这个基本问题在GitHub 问题 #4568中讨论。
至于从函数脚本内部传递/重新打包错误:
非终止错误会自动通过。
-ErrorAction Ignore
如果需要,您可以使用或来抑制它们,也可以选择使用公共参数(与 结合)2>$null
收集它们以供以后处理。-ErrorVariable
-ErrorAction SilentlyContinue
在默认情况下整个调用堆栈与您的代码一起终止的意义上,脚本终止错误被传递。
语句终止错误被写入错误流,但默认情况下您的脚本/函数会继续运行。
用于
try { ... } catch { throw }
将它们变成脚本终止错误,或者......使用
$PSCmdlet.ThrowTerminatingError($_)
而不是throw
将错误作为语句终止一个。
进一步阅读:
Guidance on when to emit a terminating vs. a non-terminating error is in this answer.
A comprehensive overview of PowerShell's error handling is in GitHub docs issue #1583.
推荐阅读
- c - 如何在c中逐位连接?
- javascript - How do client side applications work?
- java - Android 平台上的 Java String 内存使用情况
- docusignapi - 尝试使用docusign API获取authToken时出现错误请求
- javascript - 或者可以在 html 中加密或隐藏 .js 文件内容
- python - 使用 NaN 在 pandas 中按列对数据进行 Winsorizing
- javascript - 从元素中删除类不会触发 CSS 动画
- c# - (UWP) 如何从 DataTemplate 中访问我的代码隐藏文件?
- php - Call a session inside a function with object oriented php
- python - 带有 Gunicorn 的 Flask 应用程序在 Nginx 服务器上 POST 错误