因此,考虑到有四种 * 报告错误的方式(不包括 $ErrorActionPreference
、 try/catch
和 trap
块的影响,这些块有可能改变报告错误的方式),因此 PowerShell 错误报告似乎是一团糟由另一个来源提供),其中存在细微且缺乏记录的差异,更糟糕的是,没有关于何时使用不同类型的真正说明。 (我意识到它因情况而异,但“标准”是什么 - 其他人会期待我做什么?)
不过,这并不是真正的重点。我决定尽可能使用最简单的选项,因为我希望大多数其他人都会使用它,所以至少我们会保持一致性 - 但我要么没有正确理解某些东西,要么它坏了。
根据PowerShell documentation , Activity
ErrorCategoryInfo
的属性(property)类(在 CategoryInfo
的 System.Management.Automation.ErrorRecord
属性中观察到)应该是“遇到错误的操作的文本描述”。考虑到该值是可设置的,并且(在编译 cmdlet 的情况下)除非另有说明,否则会指示 cmdlet 的名称,我希望在基于脚本的错误报告方法中具有相同的功能,但我做错了什么或者我在 PowerShell 中发现了一个自 PowerShell 1.0 以来就普遍存在的错误。
考虑以下示例:
using namespace System.Management.Automation;
function Invoke-Error1 {
[CmdletBinding()]
param()
process {
$ex = [InvalidOperationException]::new()
$er = [ErrorRecord]::new($ex, 'OperationTest', [ErrorCategory]::InvalidOperation, 'MyErrorTarget')
$er.CategoryInfo.Activity = 'MyActivity'
Write-Error -ErrorRecord $er
}
}
调用此函数会导致以下错误。invoke-error1 : Operation is not valid due to the current state of the object.
At line:1 char:1
+ invoke-error1
+ ~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (MyErrorTarget:String) [Write-Error], InvalidOperationException
+ FullyQualifiedErrorId : OperationTest,Invoke-Error1
在进一步调查时,您会注意到 CategoryInfo.Activity
属性已替换为 Write-Error
.我设置的值(“MyActivity”)被忽略。如果这是预期的,我不会期望 Write-Error 方法上的参数来设置错误类别 - 但似乎存在一个。 (见 Get-Help Write-Error -Parameter CategoryActivity
。我看不出这可能是为了做什么。)这可以通过以下功能确认。
function Invoke-Error2 {
[CmdletBinding()]
param()
process {
$Params = @{
Exception = [InvalidOperationException]::new()
ErrorId = 'OperationTest'
Category = [ErrorCategory]::InvalidOperation
TargetObject = 'MyErrorTarget'
CategoryActivity = 'MyActivity'
}
Write-Error @Params
}
}
报告的错误在所有方面都与 Invoke-Error1
中的错误相同除了 cmdlet 的名称和关联的 InvocationInfo。特别是,再次注意 $Error[0].CategoryInfo.Activity -eq 'Write-Error'
而不是“我的事件”。然而,有一种编写错误的方法可以正确地将错误事件写入错误......但它似乎是报告错误的推荐方法列表中最低的,因为它通常与 Write-Error 相同.
function Invoke-Error3 {
[CmdletBinding()]
param()
process {
$ex = [InvalidOperationException]::new()
$er = [ErrorRecord]::new($ex, 'OperationTest', [ErrorCategory]::InvalidOperation, 'MyErrorTarget')
$er.CategoryInfo.Activity = 'MyActivity'
$PSCmdlet.WriteError($er)
}
}
此功能在所有方面都与 Invoke-Error1
相同除了我使用 $PSCmdlet.WriteError
而不是 Write-Error
.根据 certain community standards ,最好使用内置函数而不是 .NET 调用,当然,当行为不同时,使用产生所需结果的过程是有意义的。这是与任何已编译的 cmdlet 写入错误的方式最相似的方法。因此,cmdlet 写入的任何错误都将使用手动设置的
Activity
值,或使用 cmdlet 名称的默认值。我相信后者是 Write-Error
发生的事情。 .话虽如此,我不觉得我在这里遗漏了什么。
CategoryInfo
属性(property)及相关CategoryActivity
Write-Error
的参数自 PowerShell 1.0 起就已存在。当然,如果此功能从未起作用,那么现在有人会注意到吗?那么我做错了什么?我将如何正确报告错误?回到 Activity
的目的ErrorCategoryInfo
的属性(property), "Write-Error"不是遇到错误的操作 - 我的函数做了,我想用 Write-Error
报告它.这可能吗?*如果您不知道,可以从 PowerShell 脚本或脚本函数报告的错误如下:
throw
)$PSCmdlet.ThrowTerminatingError
)Write-Error
)$PSCmdlet.WriteError
)。这很难与 Write-Error
区分开来。但行为确实不同 - 请注意这个问题,以及 $ErrorActionPreference
的事实不影响 $PSCmdlet.WriteError
直接发出的错误的行为(尽管 $ErrorActionPreference
会影响从 $PSCmdlet.WriteError
间接发出的错误的行为,如果直接来源是脚本)。这可以通过设置 $ErrorActionPreference
来测试在继续调用 $PSCmdlet.WriteError
的函数中对比 Write-Error
. $ErrorActionPreference
变成语句终止错误。或 -ErrorAction
通用参数。 $?
变量可用于错误报告,但根据该变量的结果变化程度,这样做似乎是一种糟糕的方法。 try/catch
块可用于捕获错误并通过 $PSItem
访问它们。变量,但在该块中,如果您从模块中定义的函数执行,您可能会或可能不会通过 $Error
看到错误取决于报告错误的命令是否在您的模块中定义。编写捕获异常的渗透可能会产生两个错误添加到 $global:Error
变量 - 我希望被忽略的捕获异常将放置在 $global:Error
中如果捕获它的命令是在不同于引发错误的模块和我报告的新错误的模块中定义的。 最佳答案
首先,感谢您的深入分析。
您已经找到了一种有效的解决方法,该方法也适用于 Windows PowerShell 以实现您的自定义 .Activity
荣誉值:使用 $PSCmdlet.WriteError()
- 但请注意 $PSCmdlet
仅适用于 advanced functions和脚本。
那个.Activity
如果 value 是 System.Management.Automation.ErrorRecord
的一部分,则不兑现。实例完全传递给 Write-Error
的 -ErrorRecord
参数,而现在是如果您使用 -CategoryActivity
PowerShell (Core)7+ 中的参数(更短的别名:-Activity
)可以说是一个错误,我鼓励您在 PowerShell GitHub repo 报告。 .
注意:正如您所指出的,以下 替代解决方法 是 仅在 PowerShell (Core) 7+ 中有效 :
略不太模糊的解决方法 不需要
$PSCmdlet
因此也可用于非高级函数和脚本:using namespace System.Management.Automation
$ex = [InvalidOperationException]::new()
$er = [ErrorRecord]::new($ex, 'OperationTest', [ErrorCategory]::InvalidOperation, 'MyErrorTarget')
$er.CategoryInfo.Activity = 'MyActivity'
# Workaround: Specify the activity *also* via -Activity
Write-Error -ErrorRecord $er -Activity $er.CategoryInfo.Activity
注意:要查看结果错误(记录)的友好表示,请将上述内容通过管道传输到2>&1 | Format-List -Force
;在 PowerShell (Core) 7+ 中,您也可以使用专用的 Get-Error
小命令。或者,您可以 绕过
[ErrorRecord]
的显式构造共并使用不同参数的组合来实现相同的结果:Write-Error -Exception InvalidOperationException `
-ErrorId OperationTest `
-Activity MyActivity `
-TargetObject MyErrorTarget
I've found a bug in PowerShell that has pervaded since PowerShell 1.0.
它看起来像。
最可能的解释是直接构造
[ErrorRecord]
在 PowerShell 代码中需要高级知识并且很少见 - 事实上,让 Write-Error
为您构建这样一个实例是其主要目的,提供熟悉的带参数的 cmdlet 的命令行体验,如上面的最后一个解决方法所示。另一个因素可能是处理错误记录的调用者很少关注
.CategoryInfo.Activity
等字段。 ;通常,它是 .ErrorId
字段或所考虑的包装异常的 .NET 类型。从更哲学的角度来说,人们可能会争辩说
ErrorRecord
类型有点过度设计。至于你更一般的观察:
there's no real instruction on when to use the different types [of errors]
throw
在 PowerShell 代码中只创建前者,而 .ThrowTerminatingError()
(通常仅在 C# 代码中使用)只创建后者是一种令人困惑的不对称 - this comment on GitHub issue #14819在 future 的 PowerShell 版本中,放弃这种区别而只提供脚本终止错误,即唯一一种终止错误将是默认情况下致命的错误。这样做会:-ErrorAction
parameter 之间令人困惑的不对称性对语句终止错误没有影响,而 preference variable $ErrorActionPreference
,当设置为 'Stop'
, 确实(将它们提升为脚本终止错误)。 关于PowerShell ErrorRecord.CategoryInfo.Activity (CategoryActivity),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66751961/