2013-07-25 71 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異常現在將終止。