Welcome to Our Website

Tutto quello che avreste voluto sapere sulle eccezioni

  • 05/23/2020
  • 14 minuti a leggere
    • j
    • f

la gestione degli errori è solo una parte della vita, quando si tratta di scrivere codice. Spesso possiamo controllare e validarecondizioni per il comportamento previsto. Quando accade l’imprevisto, ci rivolgiamo alla gestione delle eccezioni., Youcan facilmente gestire le eccezioni generate dal codice di altre persone o è possibile generare il ownexceptions per gli altri a gestire.

Nota

La versione originale di questo articolo è apparso sul blog scritto da @KevinMarquette. Il team di PowerShell ringrazia Kevin per aver condiviso questo contenuto con noi. Si prega di controllare il suo blog atPowerShellExplained.com.

Terminologia di base

Abbiamo bisogno di coprire alcuni termini di base prima di saltare in questo.

Eccezione

Un’eccezione è come un evento che viene creato quando la normale gestione degli errori non può gestire il problema.,Cercando di dividere un numero per zero o esaurendo la memoria sono esempi di qualcosa che crea anexception. A volte l’autore del codice che stai utilizzando crea eccezioni per determinati problemiquando accadono.

Lancia e cattura

Quando si verifica un’eccezione, diciamo che viene generata un’eccezione. Per gestire un’eccezione lanciata, tubisogno di prenderlo. Se viene generata un’eccezione e non viene rilevata da qualcosa, lo script stopsexecuting.

Lo stack di chiamate

Lo stack di chiamate è l’elenco delle funzioni che si sono chiamate a vicenda., Quando viene chiamata una funzione, essaviene aggiunto allo stack o alla parte superiore dell’elenco. Quando la funzione esce o ritorna, viene rimossadallo stack.

Quando viene generata un’eccezione, lo stack di chiamate viene controllato in modo che un gestore di eccezioni possa intercettare.

Errori di terminazione e non terminazione

Un’eccezione è generalmente un errore di terminazione. Un’eccezione generata viene catturata o itterminates l’esecuzione corrente. Per impostazione predefinita, un errore non terminante viene generato da Write-Error e aggiunge un errore al flusso di output senza generare un’eccezione.,

Lo faccio notare perchéWrite-Error e altri errori non terminanti non attivanocatch.

Ingoiare un’eccezione

Questo è quando si cattura un errore solo per sopprimerlo. Fatelo con cautela perché può rendere i problemi di doubleshooting molto difficili.

Sintassi di base dei comandi

Ecco una rapida panoramica della sintassi di base per la gestione delle eccezioni utilizzata in PowerShell.

Lancia

Per creare il nostro evento di eccezione, generiamo un’eccezione con la parola chiavethrow.,

function Start-Something{ throw "Bad thing happened"}

Questo crea un’eccezione di runtime che è un errore di terminazione. È gestito da un catch in una funzione di chiamata o esce dallo script con un messaggio come questo.

Write-Error-ErrorAction Stop

Ho detto cheWrite-Error non genera un errore di terminazione per impostazione predefinita. Se si specifica-ErrorAction Stop,Write-Errorgenera un errore di terminazione che può essere gestito con uncatch.,

Write-Error -Message "Houston, we have a problem." -ErrorAction Stop

Grazie a Lee Dailey per aver ricordato di usare -ErrorAction Stop in questo modo.

Cmdlet-ErrorAction Stop

Se si specifica-ErrorAction Stop su qualsiasi funzione avanzata o cmdlet, si trasformano tutte le istruzioniWrite-Errorin errori di terminazione che interrompono l’esecuzione o che possono essere gestiti da uncatch.,

Start-Something -ErrorAction Stop

Try/Catch

Il modo in cui la gestione delle eccezioni funziona in PowerShell (e in molte altre lingue) è che primatrycome sezione di codice e se genera un errore, puoicatch. Ecco un rapido esempio.

Lo script catch viene eseguito solo se si verifica un errore di terminazione. Se tryviene eseguito correttamente, thenit salta ilcatch.,

Try/Finally

A volte non è necessario gestire un errore ma è comunque necessario eseguire del codice se si verifica un’eccezione o meno. Uno scriptfinally fa esattamente questo.

Dai un’occhiata a questo esempio:

Ogni volta che apri o ti connetti a una risorsa, dovresti chiuderla. SeExecuteNonQuery() genera un’eccezione, la connessione non viene chiusa. Ecco lo stesso codice all’interno di un blocco try/finally.

In questo esempio, la connessione viene chiusa se si verifica un errore. È anche chiuso se non c’èerror., Lo scriptfinally viene eseguito ogni volta.

Poiché non stai rilevando l’eccezione, viene comunque propagata nello stack delle chiamate.

Try/Catch/Finally

È perfettamente valido usarecatch efinally insieme. La maggior parte delle volte userai uno o l’altro, ma potresti trovare scenari in cui usi entrambi.

PS PSItem

Ora che abbiamo ottenuto le basi di mezzo, possiamo scavare un po ‘ più a fondo.,

All’interno del bloccocatch, c’è una variabile automatica ($PSItem o$_) di tipoErrorRecord che contiene i dettagli sull’eccezione. Ecco una rapida panoramica di alcune delle proprietà chiave.

Per questi esempi, ho usato un percorso non valido in ReadAllText per generare questa eccezione.

::ReadAllText( '\\test\no\filefound.log')

PSItem.toString ()

Questo ti dà il messaggio più pulito da usare nella registrazione e nell’output generale., ToString() viene chiamato automaticamente se $PSItem è posizionato all’interno di una stringa.

catch{ Write-Output "Ran into an issue: $($PSItem.ToString())"}catch{ Write-Output "Ran into an issue: $PSItem"}

PS PSItem.InvocationInfo

Questa proprietà contiene informazioni aggiuntive raccolte da PowerShell sulla funzione o scriptwhere l’eccezione è stata generata. Ecco il InvocationInfo dall’eccezione di esempio che Icreated.

I dettagli importanti qui mostrano il ScriptName, il Line del codice e il ScriptLineNumberdove è iniziata l’invocazione.

PS PSItem.,ScriptStackTrace

Questa proprietà mostra l’ordine delle chiamate di funzione che ti hanno portato al codice in cui è stata generata l’eccezione.

Sto solo effettuando chiamate a funzioni nello stesso script, ma questo traccerebbe le chiamate se fossero coinvolti multiplescripts.

PS PSItem.Eccezione

Questa è l’eccezione effettiva che è stata generata.

PS PSItem.Eccezione.Message

Questo è il messaggio generale che descrive l’eccezione ed è un buon punto di partenza whentroubleshooting. La maggior parte delle eccezioni ha un messaggio predefinito, ma può anche essere impostata su qualcosa di personalizzato quando viene generata l’eccezione.,

PS> $PSItem.Exception.MessageException calling "ReadAllText" with "1" argument(s): "The network path was not found."

Questo è anche il messaggio restituito quando si chiama$PSItem.ToString()se non c’era un set sulErrorRecord.

PS PSItem.Eccezione.Le eccezioni InnerException

possono contenere eccezioni interne. Questo è spesso il caso quando il codice che stai chiamandocatches un’eccezione e genera un’eccezione diversa. L’eccezione originale è collocata all’internola nuova eccezione.

PS> $PSItem.Exception.InnerExceptionMessageThe network path was not found.

Lo rivisiterò più tardi quando parlerò di re-throwing eccezioni.

PS PSItem.Eccezione.,StackTrace

Questo èStackTrace per l’eccezione. Ho mostrato unScriptStackTrace sopra, ma questo è per le chiamate al codice gestito.

at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)at System.IO.StreamReader..ctor(String path, Encoding encoding, Boolean detectEncodingFromByteOrderMarks, Int32 bufferSize, Boolean checkHost)at System.IO.File.InternalReadAllText(String path, Encoding encoding, Boolean checkHost)at CallSite.Target(Closure , CallSite , Type , String )

Si ottiene questa traccia dello stack solo quando l’evento viene generato dal codice gestito. Chiamo un .NETframework funzione direttamente in modo che è tutto quello che possiamo vedere in questo esempio. Generalmente quando stai cercando una traccia di stack, stai cercando dove il tuo codice si ferma e iniziano le chiamate di sistema.

Lavorare con le eccezioni

Ci sono più eccezioni rispetto alla sintassi di base e alle proprietà delle eccezioni.,

Cattura delle eccezioni tipizzate

Puoi essere selettivo con le eccezioni che catturi. Le eccezioni hanno un tipo e puoi specificareil tipo di eccezione che vuoi catturare.

Il tipo di eccezione viene controllato per ogni blocco catch fino a quando non viene trovato uno che corrisponde al tuo exception.It è importante rendersi conto che le eccezioni possono ereditare da altre eccezioni. Nell’esempio precedente,FileNotFoundException eredita daIOException. Quindi se IOException è stato il primo, allora verrebbe chiamato., Solo un blocco catch viene richiamato anche se ci sono più corrispondenze.

Se avessimo un System.IO.PathTooLongException, il IOException corrisponderebbe ma se avessimo unInsufficientMemoryException allora nulla lo catturerebbe e si propagherebbe nello stack.

Cattura più tipi contemporaneamente

È possibile catturare più tipi di eccezioni con la stessa istruzionecatch.

Grazie/u/Sheppard_Ra per aver suggerito questa aggiunta.

Lancio di eccezioni tipizzate

Puoi lanciare eccezioni tipizzate in PowerShell., Invece di chiamarethrow con una stringa:

throw "Could not find: $path"

Usa un acceleratore di eccezioni come questo:

throw "Could not find: $path"

Ma devi specificare un messaggio quando lo fai in questo modo.

È anche possibile creare una nuova istanza di un’eccezione da lanciare. Il messaggio è facoltativo quando lo si faquesto perché il sistema ha messaggi predefiniti per tutte le eccezioni incorporate.

throw ::new()throw ::new("Could not find path: $path")

Se non si utilizza PowerShell 5.0 o versione successiva, è necessario utilizzare l’approccio precedenteNew-Object.,

Utilizzando un’eccezione digitata, tu (o altri) puoi catturare l’eccezione in base al tipo come menzionato nella sezione precedente.

Write-Error-Exception

Possiamo aggiungere queste eccezioni tipizzate aWrite-Error e possiamo ancoracatch gli errori di exceptiontype. Usa Write-Error come in questi esempi:

Allora possiamo prenderlo in questo modo:

catch { Write-Log $PSItem.ToString()}

La grande lista di eccezioni .NET

Ho compilato una lista principale con l’aiuto della comunità Reddit/r/PowerShell che contienecentinaia di.,Eccezioni NETTE per completare questo post.

  • La grande lista di eccezioni.NET

Comincio cercando quella lista per le eccezioni che si sentono come se fossero una buona misura per mysituation. Dovresti provare a usare le eccezioni nello spazio dei nomi System di base.

Le eccezioni sono oggetti

Se inizi a utilizzare molte eccezioni digitate, ricorda che sono oggetti. Diverse eccezioniavere diversi costruttori e proprietà., Se guardiamo il FileNotFoundExceptiondocumentation per System.IO.FileNotFoundException, vediamo che possiamo passare in un messaggio e un filepath.

::new("Could not find file", $path)

E ha una proprietàFileName che espone il percorso del file.

catch { Write-Output $PSItem.Exception.FileName}

È necessario consultare la documentazione.NET per altri costruttori e proprietà dell’oggetto.

Re-gettando un’eccezione

Se tutto quello che farai nel tuo bloccocatchèthrowla stessa eccezione, allora non farlocatch., Si dovrebbe solo catch un’eccezione che si prevede di gestire o eseguire qualche azione quando ithappens.

Ci sono momenti in cui si desidera eseguire un’azione su un’eccezione, ma rieseguire l’eccezione in modo che qualcosa a valle possa gestirla. Potremmo scrivere un messaggio o registrare il problema vicino a dove lo scopriamo ma gestire il problema più in alto nello stack.

catch{ Write-Log $PSItem.ToString() throw $PSItem}

Abbastanza interessante, possiamo chiamare throwdall’interno di catch e ri-lancia la currentexception.,

catch{ Write-Log $PSItem.ToString() throw}

Vogliamo lanciare nuovamente l’eccezione per preservare le informazioni di esecuzione originali come lo script di origine e il numero di riga. Se lanciamo una nuova eccezione a questo punto, nasconde dove è iniziata l’eccezione.

Re-throwing una nuova eccezione

Se si cattura un’eccezione ma si desidera lanciarne una diversa, è necessario annidare l’originalexception all’interno di quella nuova. Ciò consente a qualcuno nello stack di accedervi come$PSItem.Exception.InnerException.

catch{ throw ::new('Could not access field',$PSItem.Exception)}

P PSCmdlet.,ThrowTerminatingError ()

L’unica cosa che non mi piace nell’usare throw per le eccezioni raw è che il messaggio di errore indica l’istruzione throw e indica che la riga è dove si trova il problema.

Avere il messaggio di errore mi dice che il mio script è rotto perché ho chiamatothrow sulla riga 31 è un messaggio abad per gli utenti del tuo script da vedere. Non dice loro nulla di utile.

Dexter Dhami ha sottolineato che posso usareThrowTerminatingError() per correggerlo.,

Se assumiamo che ThrowTerminatingError() è stato chiamato all’interno di una funzione chiamata Get-Resource, alloraquesto è l’errore che vedremmo.

Vedi come punta alla funzioneGet-Resource come fonte del problema? Questo dice all’utente qualcosa di utile.

Poiché $PSItem è un ErrorRecord, possiamo anche usare ThrowTerminatingError in questo modo per ri-lanciare.,

catch{ $PSCmdlet.ThrowTerminatingError($PSItem)}

Questo cambia la fonte dell’errore nel Cmdlet e nasconde gli interni della tua funzione dagli utenti del tuo Cmdlet.

Try può creare errori di terminazione

Kirk Munro sottolinea che alcune eccezioni terminano gli errori solo quando vengono eseguiti all’interno di un bloccotry/catch. Ecco l’esempio che mi ha dato che genera un’eccezione di runtime divide per zero.

function Start-Something { 1/(1-1) }

Quindi invocarlo in questo modo per vederlo generare l’errore e comunque inviare il messaggio.,

&{ Start-Something; Write-Output "We did it. Send Email" }

Ma inserendo lo stesso codice all’interno di untry/catch, vediamo qualcos’altro accadere.

try{ &{ Start-Something; Write-Output "We did it. Send Email" }}catch{ Write-Output "Notify Admin to fix error and send email"}

Vediamo l’errore diventare un errore di terminazione e non emettere il primo messaggio. Quello che non mi piace di questo è che puoi avere questo codice in una funzione e agisce in modo diverso se qualcuno usa un try/catch.

Non ho avuto problemi con questo me stesso, ma è un caso d’angolo di cui essere a conoscenza.

P PSCmdlet.,ThrowTerminatingError () all’interno di try/catch

Una sfumatura di $PSCmdlet.ThrowTerminatingError() è che crea un errore di terminazione all’interno di yourCmdlet ma si trasforma in un errore non terminante dopo aver lasciato il Cmdlet. Questo lascia il burdenon il chiamante della tua funzione per decidere come gestire l’errore. Possono trasformarlo in un errore di terminazione usando -ErrorAction Stop o chiamandolo da try{...}catch{...}.,

Modelli di funzioni pubbliche

Un ultimo modo che ho avuto con la mia conversazione con Kirk Munro è stato quello di posizionare untry{...}catch{...} intorno a ognibegin,process eend blocca in tutte le sue funzioni avanzate. In quei blocchi catch generici, ha una singola riga usando $PSCmdlet.ThrowTerminatingError($PSItem) per gestire tutte le eccezioni che lasciano le sue funzioni.

function Start-Something{ param() process { try { ... } catch { $PSCmdlet.ThrowTerminatingError($PSItem) } }}

Poiché tutto è in un’istruzione try all’interno delle sue funzioni, tutto agisce in modo coerente., Thisalso dà errori puliti all’utente finale che nasconde il codice interno dall’errore generato.

Trappola

Mi sono concentrato sull’aspetto delle eccezionitry/catch. Ma c’è una caratteristica legacy che devo menzionareprima di concludere questo.

A trap viene inserito in uno script o una funzione per rilevare tutte le eccezioni che si verificano in quell’ambito. Quando si verifica un’eccezione, il codice nel trap viene eseguito e quindi il codice normale continua. Se si verificano più eccezioni, la trappola viene chiamata più e più volte.,

Personalmente non ho mai adottato questo approccio, ma posso vedere il valore negli script di amministratore o controller che registrano tutte le eccezioni, quindi continuano a essere eseguiti.

Note di chiusura

L’aggiunta di una corretta gestione delle eccezioni agli script non solo li rende più stabili, ma rende anche più facile risolvere tali eccezioni.

Ho passato molto tempo a parlarethrow perché è un concetto fondamentale quando si parla di exceptionhandling., PowerShell ci ha anche dato Write-Errorche gestisce tutte le situazioni in cui dovresti usarethrow. Quindi non pensare che devi usare throw dopo aver letto questo.

Ora che ho avuto il tempo di scrivere sulla gestione delle eccezioni in questo dettaglio, ho intenzione di passare all’utilizzo diWrite-Error -Stop per generare errori nel mio codice. Ho anche intenzione di prendere il consiglio di Kirk e fare ThrowTerminatingError il mio gestore di eccezioni goto per ogni funzione.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *