2013-07-25 88 views
11

在此great article Keith解释Powershell中终止错误和非终止错误之间的区别。根据Keith从调用.NET对象或类型成员中抛出的异常是非终止错误。是.net方法的异常终止或不终止错误?

事实上,如果我们定义这个.NET类来进行测试:

$a = Add-Type 'public class bla { public static void bl() { throw new System.ApplicationException("test"); }}' -PassThru 

然后将此功能:

function tst { 1 | write-host; $a::bl(); 2 | Write-host } 

我们会看到,当TST功能被称为例外似乎是不终止:第二个Write-Host的作品。

但想一想:

function tst2 { try { tst } catch { "Catch!" } } 

如果我们打开the documentation,我们可以读到,抓响应或脚本处理终止错误。在整篇文章中,文章处理的错误在许多地方被认定为“终止”。

所以当我们运行上面的第二个写主机不运行,但catch块的确如此。看来,我们的非终止错误突然终止。

怎么回事?

另一种看法是,具有良好的老陷阱,它仍然非终止错误:

function tst3 { tst trap { "Trap!" } } 

现在,从实用的角度来看,我想达到如下。在一段代码中,我想终止.NET代码抛出的异常。我希望从cmdlet中终止错误并终止cmdlet的非终止错误,并且不终止。

我该如何做到这一点?

例子:

Do-Something 
Call::Something() 
Do-SomethingElse 
Call::SomethingElse() 
Do-YetMoreSomething 
Call::YetMoreSomething() 

我想对所有的异常终止,从.NET上述呼吁。我也想终止从cmdlet中终止错误。我不想终止来自cmdlet的非终止错误。

+0

@keithhill你能帮忙吗?谢谢! –

+0

是不是可以通过$ ErrorActionPreference来定义?不是代码的实际终止行为,而是脚本如何处理它......? –

回答

9

是的,这是今年早些时候发布在PowerShell MVP电子邮件列表上的。 PowerShell根据是否存在外部try/catch来更改.NET异常的错误处理行为。这只是猜测,但我猜这是为了简单的脚本方案。也就是说,如果脚本管理员(管理员)调用.NET方法并产生异常,PowerShell团队不希望停止执行整个脚本。一旦V2出现并介绍了适当的try/catch,我猜他们必须重新审视这个决定,并提出当前的妥协方案。

这就是说,解决这个问题是你发现的一个痛苦。您可以在脚本级别将$ ErrorActionPreference设置为Stop,然后对于每个可能生成非终止错误的cmdlet使用参数-ErrorAction Continue。或者,您可以将所有.NET调用放入高级函数中,然后使用参数-ErrorAction Stop调用该函数。我希望有一个更好的答案,但在审查了MVP线程后,我没有看到任何更好的解决方案。

+0

我认为有更好的解决方案,查看我的答案。这可能有些兴趣。 – alecov

+0

@Alek你的解决方案或多或少是OP用tst2函数演示的内容,以及我用这句话解释的内容'PowerShell根据是否存在外部try/catch来改变它对于.NET异常的错误处理行为。鉴于模块中包含所有脚本的许多潜在功能,try/finally中的所有脚本都是PITA。这里真正的诀窍是找到一种改变行为的全球方式。如果你发现“那个”的伎俩,让我知道。 ;-) –

1

一些测试后,似乎抛出异常try/catch块以一种特殊的方式,这既不是一个终接,也不是非终止错误行为。

比较下面的输出:

# Functions which create simple non-terminating errors. 
function E1 { [CmdletBinding()] param() Write-Error "Error"; "E1" } 
function E2 { [CmdletBinding()] param() Write-Error "Error" -ErrorAction:Stop; "E2" } 

# Functions which throw .NET exceptions, inside and outside try/catch blocks. 
function F1 { [CmdletBinding()] param() [DateTime]""; "F1" } 
function F2 { [CmdletBinding()] param() try { [DateTime]"" } catch { throw } "F2" } 

# Test functions. 
function TestE1 { [CmdletBinding()] param() E1; "TestE1" } 
function TestF1 { [CmdletBinding()] param() F1; "TestF1" } 
function TestE2 { [CmdletBinding()] param() E2; "TestE2" } 
function TestF2 { [CmdletBinding()] param() F2; "TestF2" } 
function StopE1 { [CmdletBinding()] param() E1 -ErrorAction:Stop; "StopE1" } 
function StopF1 { [CmdletBinding()] param() F1 -ErrorAction:Stop; "StopF1" } 
function StopE2 { [CmdletBinding()] param() E2 -ErrorAction:Stop; "StopE2" } 
function StopF2 { [CmdletBinding()] param() F2 -ErrorAction:Stop; "StopF2" } 

# All the following call pairs display similar behavior. 
TestE1  # Returns "E1", "TestE1". 
TestF1  # Returns "F1", "TestF1". 

TestE2  # Halts and returns nothing. 
TestF2  # Halts and returns nothing. 

StopE2  # Halts and returns nothing. 
StopF2  # Halts and returns nothing. 

# The following display different behavior. 
StopE1  # Halts and returns nothing. 
StopF1  # F1 halts but StopF1 doesn't; the call returns "StopF1". 

这表明.NET抛出异常外try/catch非终止错误,或者至少不被产生的相同种类的错误Write-Error

为了实现一致的结果,唯一的方式到目前为止是catch异常,要么重新throw它(创建终止错误)或Write-Error它(创建非终止错误)。例如:

function F1 { [CmdletBinding()] param() try { [DateTime]"" } catch { Write-Error -ErrorRecord:$_ } "F1" } 

# Now everything's fine. 
TestE1  # Returns "E1", "TestE1". 
TestF1  # Returns "F1", "TestF1". 

StopE1  # Halts and returns nothing. 
StopF1  # Halts and returns nothing. 
+0

谢谢你的贡献,但我不确定我了解这是如何解决我的情况。你可以扩大一点吗? –

+0

没关系。我认为你的问题是如何在特定的代码位置管理.NET异常,而不是整个脚本。答案显示了如何使.NET异常行为像选择性地终止错误一样。我会在这里保留这个答案,希望它能帮助别人。 – alecov

1

Try块转动的.NET异常变成终止错误,所以你可以附上你的.NET调用在这样一个块:在你的例子你tst功能

Try { Class::RunSomeCode() } Catch { Throw } 

所以变成

function tst { 1 | write-host; Try { $a::bl() } Catch { Throw } 2 | Write-host } 

而你的.NET异常现在将终止。