2013-07-25 13 views
11

In questo great article Keith spiega la differenza tra gli errori di terminazione e quelli non terminanti in PowerShell. Secondo Keith, le eccezioni generate dalle chiamate a un membro di un oggetto o tipo .NET sono errori non terminanti.È un'eccezione da un metodo .net un errore di terminazione o di non terminazione?

Infatti se definiamo questa classe .net per il test:

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

E poi questa funzione:

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

vedremo che quando viene chiamata la funzione TST l'eccezione sembra essere non terminazione: il secondo Write-Host funziona.

Ma considera questo:

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

Se apriamo the documentation, possiamo leggere che il fermo risponde o gestisce gli errori che terminano in script. In tutto il testo degli articoli gli errori trattati dall'articolo sono qualificati come "terminanti" in numerosi luoghi.

Quindi quando eseguiamo la riga sopra il secondo Write-Host NON viene eseguito ma il blocco di cattura lo fa. Sembra che il nostro errore non terminante diventi improvvisamente terminante.

Come mai?

Un'altra osservazione, è che con la buona vecchia trappola è ancora non fatale errore:

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

Ora, dal punto di vista pratico, ciò che voglio ottenere è la seguente. In un blocco di codice voglio terminare le eccezioni generate dal codice .NET. Voglio lasciare gli errori di chiusura dai cmdlet che terminano e gli errori non terminanti dai cmdlet non terminanti.

Come ottengo questo?

Esempio:

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

voglio terminare su tutte le eccezioni da .net chiamate sopra. Voglio anche terminare la chiusura degli errori dai cmdlet. Non voglio terminare con errori non terminanti dai cmdlet.

+0

@keithhill puoi aiutare? Grazie! –

+0

Non è definibile da $ ErrorActionPreference? Non tanto quanto il vero comportamento di terminazione del codice, ma su come lo gestisce ...? –

risposta

9

Sì, questo è arrivato sulla mailing list MVP di PowerShell all'inizio di quest'anno. PowerShell modifica il comportamento di gestione degli errori per le eccezioni .NET a seconda che esista o meno un try/catch esterno. Questa è solo una speculazione, ma suppongo che questo fosse per semplici scenari di scripting. Cioè, se lo scripter (admin) crea un errore nel chiamare un metodo .NET e questo genera un'eccezione, il team di PowerShell non voleva che cessasse l'esecuzione dell'intero script. Una volta che la V2 è arrivata e ha introdotto il giusto try/catch, immagino che abbiano dovuto rivisitare quella decisione e che siano arrivati ​​all'attuale compromesso.

Detto questo, lavorare su questo è un dolore come hai scoperto. È possibile impostare $ ErrorActionPreference su Stop a livello di script e quindi per ogni cmdlet in grado di generare errori non di terminazione utilizzare il parametro -ErrorAction Continue. In alternativa, è possibile inserire tutte le chiamate di .NET all'interno di una o più funzioni avanzate, quindi chiamare la/e funzione/e con il parametro -ErrorAction Stop. Vorrei che ci fosse una risposta migliore, ma dopo aver esaminato quel thread MVP, non ho visto soluzioni migliori.

+0

Penso che ci sia una soluzione migliore, controlla la mia risposta. Potrebbe essere di un certo interesse. – alecov

+0

@Alek La tua soluzione è più o meno ciò che l'OP ha dimostrato con la funzione tst2 e ciò che spiego con questa frase 'PowerShell cambia il suo comportamento nella gestione degli errori per le eccezioni .NET a seconda che esista o meno un try/catch esterno. Dato molte potenziali funzioni in un modulo che avvolge tutto lo script in un try/finally è un PITA. Il vero trucco qui è trovare un modo globale per cambiare il comportamento. Se trovi "quel" trucco, fammi sapere. ;-) –

1

Dopo alcuni test, sembra che le eccezioni sollevate fuoritry/catch blocchi si comportano in un modo particolare, che non è né un terminale, né un errore non fatale.

confrontare l'output seguente:

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

Questo dimostra che le eccezioni NET gettati fuori try/catch blocchi sono non errori non fatale, o almeno non lo stesso tipo di errori generati da Write-Error.

Per ottenere risultati costanti, l'unico modo finora è quello catch l'eccezione eo ri throw esso (creando un errore fatale) o Write-Error esso (creando un errore non fatale). Per esempio:

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

Grazie per il tuo contributo, ma non sono sicuro di aver capito come questo risolva il mio scenario. Potresti espandermi un po '? –

+0

Non importa questo. Ho pensato che la tua domanda riguardasse come gestire le eccezioni .NET in percorsi di codice specifici, non nell'intero script. La risposta mostra come fare in modo che le eccezioni .NET si comportino come la chiusura di errori in modo selettivo. Terrò questa risposta qui sperando che aiuti qualcun altro. – alecov

1

Il blocco Try trasforma l'eccezione .NET in un errore fatale, in modo da poter racchiudere la chiamata .NET in un tale blocco:

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

Quindi nel tuo esempio la funzione tst diventa

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

E l'eccezione .NET ora verrà interrotta.

Problemi correlati