2013-07-25 14 views
11

W tym great article Keith wyjaśnia różnicę między błędami kończącymi i nie kończącymi się w Powershell. Według Keitha wyjątki generowane z wywołań do elementu obiektu .NET lub typu są nie kończącymi się błędami.Czy wyjątek od metody .net jest kończący się lub nie kończący się błąd?

Rzeczywiście jeśli definiujemy tej klasy .net do testowania:

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

I wówczas funkcję:

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

widzimy, że gdy TST funkcja nazywana jest wyjątek wydaje się być nie- kończenie: działa drugi Write-Host.

Ale pomyśl o tym:

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

Jeśli otworzymy the documentation, możemy przeczytać, że połów reaguje na błędy lub uchwyty kończące w skryptach. W całym tekście artykułów błędy, z którymi ma do czynienia artykuł, zostały zakwalifikowane jako "kończące się" w wielu miejscach.

Kiedy więc uruchomimy linię powyżej drugiego Write-Hosta NIE działa, ale blok catch ma. Wygląda na to, że nasz nie kończący się błąd nagle kończy się.

Jak to możliwe?

Kolejna obserwacja, że ​​ze starej dobrej pułapkę to jeszcze nie kończące błąd:

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

Teraz, z praktycznego punktu widzenia, co chcę osiągnąć jest następujące. W bloku kodu chcę zakończyć na wyjątkach wyrzuconych z kodu .NET. Chcę pozostawić kończące się błędy z kończenia cmdletów i nie kończące się błędy z cmdletów nie kończących się.

Jak to osiągnąć?

Przykład:

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

Chcę rozwiązać wszystkich wyjątków od .net zwraca powyżej. Też chcę zakończyć na kończących się błędach z cmdletów. Nie chcę kończyć się na błędach nie kończących się z cmdletów.

+0

@keithhill możesz pomóc? Dzięki! –

+0

Czy nie można tego zdefiniować za pomocą $ ErrorActionPreference? Nie tyle, co rzeczywiste zachowanie kodu, ale jak skrypt go obsługuje ...? –

Odpowiedz

9

Tak, pojawił się na liście mailingowej PowerShell MVP na początku tego roku. PowerShell zmienia zachowanie obsługi błędów w przypadku wyjątków .NET w zależności od tego, czy istnieje zewnętrzna próba/catch. To tylko spekulacja, ale domyślam się, że dotyczyło to scenariuszy z prostymi skryptami. Oznacza to, że jeśli skrypter (administrator) przestaje wywoływać metodę .NET i generuje wyjątek, zespół PowerShell nie chciał, aby zaprzestano wykonywania całego skryptu. Po pojawieniu się V2 i wprowadzeniu właściwego try/catch, zgaduję, że musieli ponownie podjąć tę decyzję i opracowali obecny kompromis.

To powiedziawszy, obejście tego problemu to ból, jaki odkryłeś. Można ustawić wartość $ ErrorActionPreference na Stop na poziomie skryptu, a następnie dla każdego polecenia cmdlet, które może generować błędy nie kończące się, należy użyć parametru -ErrorAction Continue. Możesz też umieścić wszystkie wywołania .NET w zaawansowanych funkcjach, a następnie wywołać te funkcje za pomocą parametru -ErrorAction Stop. Szkoda, że ​​nie było lepszej odpowiedzi, ale po przejrzeniu tego wątku MVP, nie widziałem żadnych lepszych rozwiązań.

+0

Myślę, że jest lepsze rozwiązanie, sprawdź moją odpowiedź. To może być interesujące. – alecov

+0

@Alek Twoje rozwiązanie jest mniej więcej tym, co OP zademonstrował przy pomocy funkcji tst2 i co wyjaśniam tym zdaniem. 'PowerShell zmienia zachowanie swoich błędów w przypadku wyjątków .NET w zależności od tego, czy istnieje zewnętrzna próba/catch'. Biorąc pod uwagę wiele potencjalnych funkcji w zawijaniu modułów, cały skrypt w próbie/końcu jest PITA. Prawdziwa sztuczka polega na znalezieniu globalnego sposobu na zmianę zachowania. Jeśli znajdziesz "tę" sztuczkę, daj mi znać. ;-) –

1

Po pewnym badań wydaje się, że wyjątki wyrzucane pozatry/catch bloki zachowują się w szczególny sposób, który nie jest ani kończącego ani nie kończące błąd.

Porównaj wyjście z następujących czynności:

# 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". 

To pokazuje, że NET wyjątki wyrzucane poza try/catch bloki są nie niekończący błędy, albo przynajmniej nie tego samego rodzaju błędów generowane przez Write-Error.

celu uzyskania powtarzalnych wyników, jedyny sposób tak daleko do catch wyjątku a następnie ponownie throw występ (tworzenie błędu terminator) lub Write-Error występ (tworzenie niekończący błędu). Na przykład:

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

Dziękuję za przyczynienie się, ale nie jestem pewien, czy rozumiem, jak to rozwiązuje mój scenariusz. Czy mógłbyś nieco rozszerzyć? –

+0

Mniejsza o to. Myślałem, że twoje pytanie dotyczyło zarządzania wyjątkami .NET w określonych lokalizacjach kodu, a nie w całym skrypcie. Odpowiedź pokazuje, jak sprawić, by wyjątki .NET działały jak wybiórcze błędy. Zatrzymam tę odpowiedź tutaj, mając nadzieję, że pomoże to komuś innemu. – alecov

1

Blok Try zamienia wyjątek .NET do błędu obciążeniowym, więc można dołączyć wezwanie .NET w takim bloku:

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

Więc w przykładzie czynność tst staje się

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

Twój wyjątek .NET będzie teraz kończący.

Powiązane problemy