PowerShell è il principale linguaggio di scripting e riga di comando utilizzato dai sistemi operativi Windows. Consente di eseguire programmi e attività di gestione del sistema da terminale ed è ampiamente utilizzato da amministratori di sistema e sviluppatori per automatizzare i loro flussi di lavoro.
La gestione degli errori è una parte importante di qualsiasi sistema di scripting e automazione. Se qualcosa va storto, i tuoi script PowerShell non dovrebbero semplicemente bloccarsi: dovrebbero identificare che si è verificato un errore e intervenire per eseguire nuovamente l’operazione o per avvisarti che qualcosa è andato storto in modo che tu possa trovare la causa e risolvere il problema.
Questa guida si propone di fornirti una comprensione dettagliata della gestione degli errori Powershell, compresi i tipi di errori, le tecniche di gestione degli errori, gli esempi di come implementare la gestione degli errori nei tuoi script e le best practice per lanciare, catturare, registrare e rispondere agli errori.
Capire i tipi di errore in PowerShell
Esistono due tipi di errore che devi gestire in modo diverso per garantire l’affidabilità dei tuoi script PowerShell.
I terminating error (errori di terminazione) interrompono l’esecuzione del comando o dello script quando si verificano. Di solito si tratta di eccezioni, che possono essere gestite con i blocchi try/catch (spiegati più avanti in questo articolo). Le cause dei terminating error includono errori di sintassi (quando sbagli a digitare un comando o fornisci parametri non validi) ed errori interni all’applicazione o al comando in esecuzione.
I non-terminating error (errori non di terminazione) non interrompono l’esecuzione del comando o dello script: lo script PowerShell continuerà a essere eseguito dopo il verificarsi dell’errore. Di solito, i non-terminating error mostrano un messaggio di errore prima che l’esecuzione riprenda e devono essere gestiti in modo diverso dai terminating error, in modo che non causino il malfunzionamento degli script se il loro output previsto viene richiesto in seguito. Gli errori non-terminating possono essere causati da timeout di rete, file mancanti o problemi di autorizzazioni che, pur non rappresentando un’eccezione o un errore critico nell’esecuzione in sé, se non vengono gestiti, possono causare un comportamento indesiderato.
Concetti di base sulla gestione degli errori PowerShell (try, catch, finally e $ErrorActionPreference)
Gli errori PowerShell di tipo terminating e le eccezioni vengono lanciati (si verifica l’errore) e catturati (viene eseguito il codice per gestire l’errore). Gli errori non catturati causano l’interruzione dell’esecuzione dello script Powershell. Incapsulando il codice in blocchi try, catch e finally, puoi gestire questo tipo di errori.
Se il codice eseguito in un blocco try lancia un errore, il blocco di codice nel blocco catch corrispondente verrà eseguito per gestire l’errore (permettendo all’esecuzione di continuare, se possibile). Qualsiasi operazione di pulizia che desideri eseguire, indipendentemente dal fatto che si sia verificato o meno un errore, può essere aggiunta a un blocco finally. Questo viene spiegato meglio con un esempio di codice completo più avanti in questa pagina.
La gestione degli errori non-terminating avviene in modo diverso, poiché un errore non viene lanciato per interrompere l’esecuzione e quindi non può essere catturato. L’errore viene invece memorizzato nella variabile $Error, un array di errori a cui puoi accedere in qualsiasi punto degli script PowerShell per vedere se un’istruzione precedente ha generato un errore non-terminating, come mostrato in questo esempio:
Get-ChildItem -Path “C:\fake_path”
if ($Error) {
Write-Host “Error occurred: $($Error[0])” # Visualizza l’errore più recente come stringa
$Error.Clear() # Cancella l’errore
}
Sopra, il comando Get-ChildItem di PowerShell genera un errore non-terminating se il percorso del filesystem fornito non esiste (in questo caso C:\fake_path). L’errore viene registrato nella variabile $Error, che viene poi controllata e gestita.
Se vuoi assegnare l’output di errore di un particolare comando a una variabile personalizzata, puoi usare il parametro -ErrorVariable (tieni presente che in questo caso la variabile error non è un array):
Get-ChildItem -Path “C:\fake_path” -ErrorVariable customError
if ($customError) {
Write-Host “Error occurred: $customError” # Visualizza l’errore come una stringa
}
Puoi anche trasformare gli errori non-terminating in errori terminating e gestirli con blocchi try/catch. Lo puoi fare per ogni errore non-terminating incontrato nello script, impostando il parametro globale $ErrorActionPreference su Stop:
$ErrorActionPreference = “Stop”
Oppure, se vuoi farlo solo per gli errori di un singolo comando, puoi impostare il parametro -ErrorAction:
Get-ChildItem -Path “C:\fake_path” -ErrorAction Stop
Implementare i blocchi try/catch/finally in PowerShell
Di seguito è riportato un esempio di script PowerShell che chiarisce la sintassi e la struttura dei blocchi try/catch/finally, e mostra come utilizzare più blocchi catch per diversi tipi di eccezione e come utilizzare un blocco finally per ripulire e gestire eventuali errori residui. Innanzitutto, ecco un singolo errore gestito con un blocco try/catch:
try {
# Tenta di eseguire un’operazione su file che fallisce
Get-Content -Path “C:\fake_path\fake_file.txt” -ErrorAction Stop
} catch {
Write-Host “Exception caught: $($_.Exception.Message)”
}
Puoi anche gestire più tipi diversi di potenziali errori in un singolo blocco try, gestendo ciascuno con il proprio blocco catch:
try {
# Ciascuno dei comandi seguenti lancerà un errore terminating di tipo diverso
Get-Content -Path “C:\fake_path\fake_file.txt” -ErrorAction Stop # Tentativo di operazione su file che non riesce a lanciare una ItemNotFoundException
$null.fakeFunction() # Tentativo di eseguire un metodo inesistente su un oggetto null per lanciare una RuntimeException
$divideZero = 1/0 # Divide per zero per lanciare un’eccezione che non sarà catturata in modo specifico
} catch [System.Management.Automation.ItemNotFoundException] { # Cattura l’errore specifico lanciato da Get-Content
Write-Host “Item not found exception caught: $($_.Exception.Message)”
} catch [System.Management.Automation.RuntimeException] { # Cattura l’errore specifico lanciato da $null.fakeFunction
Write-Host “Runtime exception caught: $($_.Exception.Message)”
} catch { # Cattura tutti gli altri errori
# Blocco generale di cattura per qualsiasi altra eccezione
Write-Host “General exception caught: $($_.Exception.Message)”
} finally { # Il blocco finally viene sempre eseguito, indipendentemente dal fatto che sia stata lanciata un’eccezione o meno.
Write-Host “Executing the finally block.”
$Error.Clear() # Questo potrebbe essere usato, per esempio, per ripulire gli errori non-terminating.
}
Lo scopo di ogni istruzione è spiegato nel codice, in modo da permetterti di vedere il contesto completo di come le istruzioni try, catch e finally lavorano insieme. Tieni presente che se si verificano più errori terminating nello stesso blocco try, solo il primo errore verrà catturato e gestito, e poi verrà eseguito il blocco finally. Una volta risolto questo errore, gli errori successivi verranno catturati durante l’esecuzione dello script.
Tecniche avanzate di gestione degli errori PowerShell ed esempi
È anche possibile annidare i blocchi try/catch, in modo che gli errori “salgano” attraverso di essi. Ciò ti consente di gestire errori specifici nei blocchi try/catch interni e di gestire qualsiasi errore non catturato o rilanciato, anche quelli all’interno dei blocchi catch interni, nei blocchi esterni. Per esempio, potresti usare blocchi annidati per gestire errori specifici e poi usare un singolo blocco esterno per catturare e registrare tutti gli errori, invece di ripetere il codice per registrarli in ogni blocco interno.
try { # Inizio del blocco esterno di cattura
try { # Inizio del blocco interno di cattura
# Inserisci qui il codice potenzialmente causa di errori
Get-Content -Path “C:\fake_path\fake_file.txt” -ErrorAction Stop
} catch [System.Management.Automation.ItemNotFoundException] { # Dichiarazione di catch per il blocco catch interno che gestisce specificamente ItemNotFoundException
Write-Host “Inner try block file not found exception caught: $($_.Exception.Message)”
} catch { # Dichiarazione di cattura per il blocco catch interno che gestisce tutte le altre eccezioni
Write-Host “Inner try block exception caught: $($_.Exception.Message)”
throw # Lancia nuovamente l’eccezione, in modo che arrivi al blocco try/catch esterno
}
} catch { # Dichiarazione di cattura per il blocco catch esterno
Write-Host “Outer try block exception caught: $($_.Exception.Message)” # Gestisce tutti gli errori rilanciati nei blocchi interni di catch qui per le attività di pulizia, registrazione e avviso
}
Nell’esempio precedente, l’eccezione ItemNotFoundException non viene rilanciata, quindi non verrà catturata dal blocco catch esterno.
Quando iniziani a espandere gli script PowerShell per coprire i tuoi specifici casi d’uso, è probabile che tu voglia essere in grado di introdurre eccezioni e record di errorePowerShell personalizzati:
try {
$customException = New-Object Exception “This is a custom error!” # Crea e lancia un errore personalizzato
# Crea un record di errore personalizzato
$customErrorRecord = New-Object System.Management.Automation.ErrorRecord (
$customException,
“CustomErrorID”,
[System.Management.Automation.ErrorCategory]::NotSpecified, # Imposta la categoria ErrorRecord a NotSpecified
$null # Non specifica un oggetto di destinazione specifico per questo errore
)
throw $customErrorRecord
} catch {
# Cattura e gestisce il record di errore personalizzato
Write-Host “Error Message: $($_.Exception.Message)”
Write-Host “Error ID: $($_.FullyQualifiedErrorId)”
}
Sopra, puoi notare che non è stato specificato alcun oggetto di destinazione (poiché è impostato su $null). Potresti invece sostituirlo con un percorso di file o un altro riferimento e aggiungere ulteriori dettagli al testo dell’eccezione per rendere l’errore personalizzato più informativo.
Le best practice nella gestione degli errori PowerShell
Esistono diverse best practice da seguire quando scrivi gli script PowerShell, per garantire una gestione efficace degli errori:
- Scrivi messaggi di errore chiari e prevedibili: I messaggi di errore devono essere descrittivi e includere sufficienti dettagli, dall’oggetto Exception o ErrorRecord per identificare la causa specifica di un errore.
- Assicurati che gli script possano continuare a funzionare (o a uscire in modo pulito) se si verifica un errore: Se i tuoi script stanno eseguendo altre attività, potresti voler gestire l’errore mentre continuano. Gli errori che impediscono allo script di continuare dovrebbero causare l’uscita dello script con un codice di uscita e il risultato di qualsiasi errore dovrebbe essere registrato e riportato.
- Verifica regolarmente la tua infrastruttura di gestione degli errori e di reporting: Gli automatismi di PowerShell sono inutili se falliscono senza che tu te ne accorga. Testa regolarmente i tuoi script introducendo errori per assicurarti che la gestione degli errori funzioni, che i dettagli siano registrati e che tu riceva avvisi.
La gestione degli errori è una caratteristica importante dello scripting PowerShell ed è utile per sviluppare le tue capacità di scripting e automazione. L’implementazione corretta della gestione degli errori, compreso il lancio corretto degli errori, l’uso di blocchi try/catch/finally e l’utilizzo di $ErrorActionPreference, ti aiuterà a padroneggiare la sintassi di PowerShell e a comprendere meglio i problemi incontrati dagli script, aiutandoti così a gestirli meglio.