PowerShell ErrorRecord.CategoryInfo.Activity(类别活动)
PowerShell ErrorRecord.CategoryInfo.Activity (CategoryActivity)
因此,考虑到有四种*方式报告错误(不包括 $ErrorActionPreference
、try/catch
和 trap
块的影响,因此 PowerShell 错误报告看起来像一团糟这有可能改变另一个来源报告错误的方式),差异很小且记录不充分,更糟糕的是没有关于何时使用不同类型的真正说明。 (我知道这会因情况而异,但“标准”是什么——其他人会期望我做什么?)
但这并不是这里的重点。我决定尽可能使用最简单的选项,因为这是我希望大多数其他人会使用的选项,所以至少我们会有一致性 - 但我要么没有正确理解某些内容,要么它已损坏。
根据PowerShell documentation,ErrorCategoryInfo
class的Activity
属性(观察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
}
}
除了 cmdlet 的名称和关联的 InvocationInfo 之外,报告的错误在所有方面都与 Invoke-Error1
中的错误相同。具体来说,再次注意 $Error[0].CategoryInfo.Activity -eq 'Write-Error'
而不是“MyActivity”。
然而,有一种写错误的方法确实正确地写了错误activity错误...但它似乎是最低的在报告错误的推荐方法列表中,因为它 通常 与 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)
}
}
除了我使用 $PSCmdlet.WriteError
而不是 Write-Error
之外,此函数在所有方面都与 Invoke-Error1
相同。根据 certain community standards,首选使用内置函数而不是 .NET 调用,当然,当行为不同时,使用产生所需结果的过程是有意义的。
这是与任何已编译的 cmdlet 写入错误的方式最相似的方法。因此,cmdlet 写入的任何错误都将使用手动设置的 Activity
值,或使用 cmdlet 名称的默认值。我相信后者在某种程度上是 Write-Error
.
正在发生的事情
话虽这么说,但我不觉得我在这里遗漏了什么。 CategoryInfo
属性 和 Write-Error
的关联 CategoryActivity
参数自 PowerShell 1.0 以来就已存在。当然,如果此功能从未起作用,现在有人会注意到吗?那我做错了什么?我将如何正确报告错误?回到 ErrorCategoryInfo
Activity
属性 的目的,“Write-Error”不是遇到错误的操作 - 我的函数确实如此,我想报告它 使用 Write-Error
。这可能吗?
*如果您不知道,可以从 PowerShell 脚本或脚本函数报告的错误如下:
- 脚本终止(通过
throw
)
- 语句终止(通过
$PSCmdlet.ThrowTerminatingError
)
- 非终止错误(通过
Write-Error
)
- 非终止错误(通过
$PSCmdlet.WriteError
)。这很难与 Write-Error
区分开来,但确实表现不同 - 请注意这个问题,以及 $ErrorActionPreference
不会影响 $PSCmdlet.WriteError
直接发出的错误行为的事实(尽管 $ErrorActionPreference
将影响从 $PSCmdlet.WriteError
间接 发出的错误的行为,如果 direct 源是脚本)。这可以通过在你的函数中设置 $ErrorActionPreference
来测试,该函数继续调用 $PSCmdlet.WriteError
vs Write-Error
.
- 除了这些方法之外,任何非终止错误都可以通过
$ErrorActionPreference
或 -ErrorAction
公共参数转换为语句终止错误。 $?
变量可用于错误报告,但根据该变量的结果变化程度,这似乎是一种糟糕的方法。 try/catch
块可用于捕获错误并通过 $PSItem
变量访问它们,但在该块内,如果您从模块中定义的函数执行,您可能会或可能不会通过 $Error
取决于报告错误的命令是否在您的模块中定义。编写捕获异常的渗透可能会产生两个错误,这些错误被添加到 $global:Error
变量中 - 如果定义了捕获它的命令,我希望被忽略的捕获异常将被放置在 $global:Error
中在抛出错误的模块以外的模块中,以及我报告的新错误。
首先,感谢您的深入分析。
您已经找到了一种有效的变通方法 ,它也适用于 Windows PowerShell,使您的自定义 .Activity
值得到尊重:使用 $PSCmdlet.WriteError()
- though note that $PSCmdlet
is only available in advanced functions 和脚本。
如果 .Activity
值 是 System.Management.Automation.ErrorRecord
instance passed in full to Write-Error
's -ErrorRecord
parameter, whereas it now is if you use the -CategoryActivity
parameter (shorter alias: -Activity
) in PowerShell (Core)7+, is arguably a bug that I encourage you to report at the PowerShell GitHub repo 的一部分,则 不会受到尊重。
- 更新:谢天谢地,您已经在 GitHub issue #15088.
更新了
注意:正如您所指出的,以下替代解决方法 仅在 PowerShell(核心)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
cmdlet。
或者,您可以完全绕过 [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]
GitHub 文档问题 Our Error Handing, Ourselves - time to fully understand and properly document PowerShell's error handling 旨在概述 PowerShell 异常复杂且部分不一致的错误处理,并要求此类概述成为文档的正式部分。
This answer 尝试就何时使用 非终止 与 statement-terminating[=114] 提供指导=]错误。
- 有两种类型的终止错误-脚本-终止(致命) vs. 语句-终止,PowerShell代码中的
throw
只会创建前,而 .ThrowTerminatingError()
(通常仅在 C# 代码中使用)只会造成后者是一种令人困惑的不对称性 - this comment on GitHub issue #14819 使得放弃这种区别而支持仅提供 script-未来 PowerShell 版本中的终止错误 - 即唯一一种终止错误将是默认情况下的 fatal。这样做会:
- 防止脚本中的更多命令默认执行,这似乎是必要的,因为当前的语句终止错误通常是严重错误条件。
- 消除 common
-ErrorAction
parameter having no effect on statement-terminating errors, whereas preference variable $ErrorActionPreference
之间令人困惑的不对称性,当设置为 'Stop'
时,确实(将它们提升为 script-终止错误)。
因此,考虑到有四种*方式报告错误(不包括 $ErrorActionPreference
、try/catch
和 trap
块的影响,因此 PowerShell 错误报告看起来像一团糟这有可能改变另一个来源报告错误的方式),差异很小且记录不充分,更糟糕的是没有关于何时使用不同类型的真正说明。 (我知道这会因情况而异,但“标准”是什么——其他人会期望我做什么?)
但这并不是这里的重点。我决定尽可能使用最简单的选项,因为这是我希望大多数其他人会使用的选项,所以至少我们会有一致性 - 但我要么没有正确理解某些内容,要么它已损坏。
根据PowerShell documentation,ErrorCategoryInfo
class的Activity
属性(观察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
}
}
除了 cmdlet 的名称和关联的 InvocationInfo 之外,报告的错误在所有方面都与 Invoke-Error1
中的错误相同。具体来说,再次注意 $Error[0].CategoryInfo.Activity -eq 'Write-Error'
而不是“MyActivity”。
然而,有一种写错误的方法确实正确地写了错误activity错误...但它似乎是最低的在报告错误的推荐方法列表中,因为它 通常 与 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)
}
}
除了我使用 $PSCmdlet.WriteError
而不是 Write-Error
之外,此函数在所有方面都与 Invoke-Error1
相同。根据 certain community standards,首选使用内置函数而不是 .NET 调用,当然,当行为不同时,使用产生所需结果的过程是有意义的。
这是与任何已编译的 cmdlet 写入错误的方式最相似的方法。因此,cmdlet 写入的任何错误都将使用手动设置的 Activity
值,或使用 cmdlet 名称的默认值。我相信后者在某种程度上是 Write-Error
.
话虽这么说,但我不觉得我在这里遗漏了什么。 CategoryInfo
属性 和 Write-Error
的关联 CategoryActivity
参数自 PowerShell 1.0 以来就已存在。当然,如果此功能从未起作用,现在有人会注意到吗?那我做错了什么?我将如何正确报告错误?回到 ErrorCategoryInfo
Activity
属性 的目的,“Write-Error”不是遇到错误的操作 - 我的函数确实如此,我想报告它 使用 Write-Error
。这可能吗?
*如果您不知道,可以从 PowerShell 脚本或脚本函数报告的错误如下:
- 脚本终止(通过
throw
) - 语句终止(通过
$PSCmdlet.ThrowTerminatingError
) - 非终止错误(通过
Write-Error
) - 非终止错误(通过
$PSCmdlet.WriteError
)。这很难与Write-Error
区分开来,但确实表现不同 - 请注意这个问题,以及$ErrorActionPreference
不会影响$PSCmdlet.WriteError
直接发出的错误行为的事实(尽管$ErrorActionPreference
将影响从$PSCmdlet.WriteError
间接 发出的错误的行为,如果 direct 源是脚本)。这可以通过在你的函数中设置$ErrorActionPreference
来测试,该函数继续调用$PSCmdlet.WriteError
vsWrite-Error
. - 除了这些方法之外,任何非终止错误都可以通过
$ErrorActionPreference
或-ErrorAction
公共参数转换为语句终止错误。$?
变量可用于错误报告,但根据该变量的结果变化程度,这似乎是一种糟糕的方法。try/catch
块可用于捕获错误并通过$PSItem
变量访问它们,但在该块内,如果您从模块中定义的函数执行,您可能会或可能不会通过$Error
取决于报告错误的命令是否在您的模块中定义。编写捕获异常的渗透可能会产生两个错误,这些错误被添加到$global:Error
变量中 - 如果定义了捕获它的命令,我希望被忽略的捕获异常将被放置在$global:Error
中在抛出错误的模块以外的模块中,以及我报告的新错误。
首先,感谢您的深入分析。
您已经找到了一种有效的变通方法 ,它也适用于 Windows PowerShell,使您的自定义 .Activity
值得到尊重:使用 $PSCmdlet.WriteError()
- though note that $PSCmdlet
is only available in advanced functions 和脚本。
如果 .Activity
值 是 System.Management.Automation.ErrorRecord
instance passed in full to Write-Error
's -ErrorRecord
parameter, whereas it now is if you use the -CategoryActivity
parameter (shorter alias: -Activity
) in PowerShell (Core)7+, is arguably a bug that I encourage you to report at the PowerShell GitHub repo 的一部分,则 不会受到尊重。
- 更新:谢天谢地,您已经在 GitHub issue #15088. 更新了
注意:正如您所指出的,以下替代解决方法 仅在 PowerShell(核心)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
cmdlet。
或者,您可以完全绕过 [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]
GitHub 文档问题 Our Error Handing, Ourselves - time to fully understand and properly document PowerShell's error handling 旨在概述 PowerShell 异常复杂且部分不一致的错误处理,并要求此类概述成为文档的正式部分。
This answer 尝试就何时使用 非终止 与 statement-terminating[=114] 提供指导=]错误。
- 有两种类型的终止错误-脚本-终止(致命) vs. 语句-终止,PowerShell代码中的
throw
只会创建前,而.ThrowTerminatingError()
(通常仅在 C# 代码中使用)只会造成后者是一种令人困惑的不对称性 - this comment on GitHub issue #14819 使得放弃这种区别而支持仅提供 script-未来 PowerShell 版本中的终止错误 - 即唯一一种终止错误将是默认情况下的 fatal。这样做会:- 防止脚本中的更多命令默认执行,这似乎是必要的,因为当前的语句终止错误通常是严重错误条件。
- 消除 common
-ErrorAction
parameter having no effect on statement-terminating errors, whereas preference variable$ErrorActionPreference
之间令人困惑的不对称性,当设置为'Stop'
时,确实(将它们提升为 script-终止错误)。
- 有两种类型的终止错误-脚本-终止(致命) vs. 语句-终止,PowerShell代码中的