PowerShell ThrowterminationError()错误不是真的终止错误

PowerShell ThrowterminationError()错误不是真的终止错误,powershell,Powershell,这要么是bug报告的前奏,要么是关于设计逻辑和实用性的问题。 在任何情况下,一个关于如何开发具有真正终止错误的cmdlet的问题 在干净会话中,即使用$ErrorActionPreference='Continue'而不使用 尝试或陷阱,调用以下任何命令(甚至一起调用): 因此,“继续!”。 结果表明,该方法终止命令本身,但不终止调用脚本 这些只是一些人为的例子,还有很多其他的例子。 例子并不真实,但这个问题有时是真实的 请注意,-ErrorAction Stop没有帮助,它不是为此类错误而设计

这要么是bug报告的前奏,要么是关于设计逻辑和实用性的问题。 在任何情况下,一个关于如何开发具有真正终止错误的cmdlet的问题

在干净会话中,即使用
$ErrorActionPreference='Continue'
而不使用
尝试
陷阱
,调用以下任何命令(甚至一起调用):

因此,
“继续!”throwterminationgeror()的命令之后调用code>
。 结果表明,该方法终止命令本身,但不终止调用脚本

这些只是一些人为的例子,还有很多其他的例子。 例子并不真实,但这个问题有时是真实的

请注意,
-ErrorAction Stop
没有帮助,它不是为此类错误而设计的。 它影响非终止错误(
WriteError()
写入错误


还有一个实际问题:有没有一种方法可以发出终止错误 正在终止当前命令和调用脚本

在脚本中,这看起来可以(在某些情况下,不是全部):

请注意,
抛出“message”
实际上是终止。 但是
写入错误
throwterminingerror()
有重要区别。 它的错误位置指向
throw
行。这并不总是像以前那样有用 命令调用失败的行,特别是在语法或 输入问题,即按性质终止问题

所以脚本有一些东西。但是cmdlet可以做什么呢? 可用的
WriteError()
未终止。 原始
throw
throwterminingerror()
没有太大区别


更新

实际上,
$ErrorActionPreference='Stop'
使
通过TerminationError()
在调用脚本中终止。所以请尝试
陷阱


这不是我要找的。我特别指出了
$ErrorActionPreference
='Continue'
,默认设置。我在寻找绝对终止错误 不管是否使用默认设置。相比之下,在脚本中,抛出总是 终止(尽管它不是完美的,如前所述)。我在找那个 .NET cmdlet中的模拟


用户忘记设置
$ErrorActionPreference='Stop'
。我有时会忘记这一点。 有些情况下,比如无效命令,应该停止调用脚本 设置。至少命令作者应该能够选择此选项

“结果,”接着说在使用
throwterminingerror()的每个命令之后调用。结果表明,该方法终止命令本身,但不终止调用脚本

当然不是-您的
$ErrorActionPreference
设置为
Continue
-因此当cmdlet调用引发错误时,repl(或脚本)只需继续(Continues)执行

$ErrorActionPreference
是其定义范围的本地项,因此您可以在函数或脚本中设置它以覆盖用户的首选项(但仅在函数/脚本的范围内):

script.ps1
它将在
Save Module
出错后立即返回(即使调用范围中
$ErrorActionPreference
设置为
Continue

您可以在PowerShell throw中抛出与
throw
语句相同的异常:

Add-Type -TypeDefinition @‘
    using System;
    using System.Management.Automation;
    [Cmdlet(VerbsDiagnostic.Test, "Throw")]
    public class TestThrow : Cmdlet {
        protected override void ProcessRecord() {
            throw new RuntimeException("Message") { WasThrownFromThrowStatement=true };
        }
    }
’@ -PassThru|Select-Object -First 1 -ExpandProperty Assembly|Import-Module

Test-Throw; 'Not printed'
在脚本中,可以这样做(需要调用内部方法):

对于C#cmdlet,似乎同样需要:

否则,它会将整个语句下划线为错误,而不仅仅是一个命令:

Test-Throw | % { Some other command }; 'Not printed'

这不是我要找的。我特别提到了
$ErrorActionPreference='Continue'
。无论默认设置是否正确,我都在查找绝对终止错误。例如,在脚本中,抛出总是终止。我正在寻找.NET cmdlet中的类似版本。用户忘记设置
$ErrorActionPreference='Stop'
。我有时会忘记这一点。在某些情况下,例如无效命令,不管设置如何,都应该停止调用脚本。至少authours应该能够选择此选项。@RomanKuzmin您可以在任何子作用域内设置它,而不影响调用作用域,请参见“应答器更新”。我个人认为当前行为是一个bug或设计缺陷。在将此作为bug或建议提交之前,我想问一下这种设计的合理性或有用性。@RomanKuzmin原因很简单-安全性。通过允许一段代码的作者退出调用者范围,您颠覆了用户控件,坏的ideaIt看起来是一个可能的答案。让我在各种场景中尝试一下。遗憾的是,
$PSCmdlet.ThrowTerminatingError()
仍然没有使用这种适用于.NET cmdlet的技巧来终止。找到解决办法也很好。有什么神奇的想法吗?@RomanKuzmin脚本是可能的,但它需要使用内部方法。如果不是太糟糕的话,知道它会很有趣。至少为了更好的理解。
[CmdletBinding()]
param()

$ErrorActionPreference = 'Stop'

# script module, missing path
Save-Module InvokeBuild -Path zzz -ErrorAction Stop; 'Continued!'

# cmdlet, invalid data
ConvertFrom-Json zzz -ErrorAction Stop; 'Continued!'

# cmdlet, invalid command syntax
Get-Module -Name zzz -FullyQualifiedName zzz -ErrorAction Stop; 'Continued!'
Export-Csv -Path zzz -LiteralPath zzz -InputObject 1 -ErrorAction Stop; 'Continued!'
Add-Type -TypeDefinition @‘
    using System;
    using System.Management.Automation;
    [Cmdlet(VerbsDiagnostic.Test, "Throw")]
    public class TestThrow : Cmdlet {
        protected override void ProcessRecord() {
            throw new RuntimeException("Message") { WasThrownFromThrowStatement=true };
        }
    }
’@ -PassThru|Select-Object -First 1 -ExpandProperty Assembly|Import-Module

Test-Throw; 'Not printed'
function Test-ScriptThrow {
    $ErrorRecordType = [System.Management.Automation.ErrorRecord]
    $ErrorRecord = $ErrorRecordType::new([Exception]::new("Message"), 'ErrorId', 'NotSpecified', $null)
    $ErrorRecordType.InvokeMember('SetInvocationInfo', 'Instance, NonPublic, InvokeMethod', $null, $ErrorRecord, $MyInvocation)
    throw $ErrorRecord
}

Test-ScriptThrow; 'Not printed'
Add-Type -TypeDefinition @‘
    using System;
    using System.Reflection;
    using System.Management.Automation;
    [Cmdlet(VerbsDiagnostic.Test, "Throw2")]
    public class TestThrow2 : PSCmdlet {
        protected override void ProcessRecord() {
            ErrorRecord errorRecord = new ErrorRecord(new Exception("Message"), "ErrorId", ErrorCategory.NotSpecified, null);
            typeof(ErrorRecord).InvokeMember("SetInvocationInfo", BindingFlags.Instance|BindingFlags.NonPublic|BindingFlags.InvokeMethod, null, errorRecord, new[] { MyInvocation });
            throw new RuntimeException(null, null, errorRecord) { WasThrownFromThrowStatement=true };
        }
    }
’@ -PassThru|Select-Object -First 1 -ExpandProperty Assembly|Import-Module

Test-Throw2 | % { Some other command }; 'Not printed'
Test-Throw | % { Some other command }; 'Not printed'