Aller au contenu principal

Gérer les erreurs : try, catch, finally

Objectifs de la séance

  • Distinguer une erreur bloquante d'une erreur non bloquante
  • Utiliser try/catch/finally pour réagir proprement à un échec
  • Lire les informations d'une exception avec $_
  • Journaliser les erreurs plutôt que de les laisser s'afficher en silence

Notions théoriques

Deux familles d'erreurs

PowerShell distingue deux types d'erreurs, une notion absente en Bash et centrale ici :

  • les erreurs non bloquantes (non-terminating) : la commande affiche une erreur en rouge, mais le script continue son exécution. C'est le comportement par défaut de la plupart des cmdlets — Get-Service NomInexistant en est un exemple.
  • les erreurs bloquantes (terminating) : l'exécution du script s'arrête immédiatement à cet endroit, sauf si elle est interceptée par un try/catch.

-ErrorAction

Le paramètre commun -ErrorAction contrôle le comportement d'une commande face à une erreur non bloquante :

ValeurEffet
Continuecomportement par défaut : affiche l'erreur, poursuit l'exécution
SilentlyContinuemasque l'erreur, poursuit l'exécution
Stoptransforme l'erreur en erreur bloquante
Inquiredemande confirmation à l'utilisateur
attention

-ErrorAction Stop est la clé de voûte de la gestion d'erreurs en PowerShell : sans lui, un try/catch autour d'une cmdlet ne se déclenche pas, car l'erreur reste non bloquante et le script continue simplement après la ligne fautive.

try {
Get-Service -Name "NomInexistant" -ErrorAction Stop
}
catch {
Write-Host "Le service n'existe pas."
}

$ErrorActionPreference

Plutôt que de répéter -ErrorAction Stop sur chaque ligne, une variable de préférence globale peut s'appliquer à tout le script :

$ErrorActionPreference = 'Stop'

C'est l'équivalent PowerShell du set -e de Bash niveau 1 : toute erreur, même non bloquante à l'origine, devient attrapable par un try/catch.

try / catch / finally

try {
# code susceptible d'échouer
}
catch {
# exécuté seulement si une erreur bloquante survient dans le try
}
finally {
# exécuté systématiquement, erreur ou pas
}

Le bloc finally sert typiquement à libérer une ressource (fermer une session distante, un fichier), qu'une erreur soit survenue ou non.

L'objet erreur : $_

À l'intérieur d'un catch, la variable automatique $_ (ou son alias $PSItem) contient l'exception qui vient d'être interceptée :

catch {
Write-Host "Erreur : $($_.Exception.Message)"
Write-Host "Type : $($_.Exception.GetType().FullName)"
Write-Host "Ligne : $($_.InvocationInfo.ScriptLineNumber)"
}

$_.Exception.Message est la propriété la plus utile au quotidien : le texte lisible de l'erreur.

Attraper un type précis

Plusieurs blocs catch peuvent se succéder, du plus précis au plus général — chaque erreur en PowerShell est un objet .NET typé, ce qui permet de réagir différemment selon le type rencontré :

try {
Restart-Service -Name "Spooler" -ErrorAction Stop
}
catch [System.UnauthorizedAccessException] {
Write-Host "Droits insuffisants."
}
catch [Microsoft.PowerShell.Commands.ServiceCommandException] {
Write-Host "Le service n'existe pas sur cette machine."
}
catch {
Write-Host "Erreur inattendue : $($_.Exception.Message)"
}
info

Cette notion de hiérarchie de types d'exceptions rejoint directement ce que vous avez vu avec les classes et l'héritage en programmation orientée objet : une exception .NET est un objet, comme un autre, avec son propre type.

throw

throw permet de lever volontairement sa propre erreur bloquante, l'équivalent d'un exit 1 en Bash mais qui peut être intercepté par un catch appelant :

if (-not (Test-Path $chemin)) {
throw "Le chemin $chemin n'existe pas."
}

À distinguer de Write-Error, qui produit une erreur non bloquante par défaut.

$Error

PowerShell conserve un historique des erreurs récentes dans le tableau automatique $Error, le plus récent en premier :

$Error[0]
$Error.Clear()

Le paramètre -ErrorVariable maVariable permet aussi de capturer l'erreur d'une commande précise dans une variable dédiée.

Journaliser

Une fonction de journalisation simple, réutilisée dans tout ce cours :

function Write-Journal {
param(
[string]$Message,
[string]$Niveau = "INFO"
)
$ligne = "[{0}] {1} : {2}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $Niveau, $Message
Add-Content -Path $journal -Value $ligne
}

Cette fonction reprend l'idée de log_info déjà écrite en Bash niveau 1, transposée en PowerShell.

Exemple pratique

# Script : Restart-ServiceSurParc.ps1
# Role : redemarrer un service sur plusieurs machines, sans s'arreter a la premiere panne

$journal = "C:\Scripts\journal.log"
$machines = @("PC01", "PC02", "SRV-FIC")

function Write-Journal {
param(
[string]$Message,
[string]$Niveau = "INFO"
)
$ligne = "[{0}] {1} : {2}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $Niveau, $Message
Add-Content -Path $journal -Value $ligne
}

foreach ($machine in $machines) {
try {
Restart-Service -Name "Spooler" -ErrorAction Stop
Write-Journal "Service Spooler redemarre sur $machine"
}
catch [System.UnauthorizedAccessException] {
Write-Journal "Droits insuffisants sur $machine" -Niveau "ERREUR"
}
catch {
Write-Journal "Echec sur $machine : $($_.Exception.Message)" -Niveau "ERREUR"
}
finally {
Write-Journal "Fin du traitement de $machine"
}
}

Test de mémorisation/compréhension


Pourquoi try { Get-Service "Inexistant" } catch { "Erreur" } n'attrape-t-il rien ?


Quel bloc s'exécute même si aucune erreur ne survient dans le try ?


Quelle expression affiche le message d'erreur à l'intérieur d'un bloc catch ?


TP pour réfléchir et résoudre des problèmes

astuce

Ce TP s'exécute sans droits particuliers sur n'importe quel poste Windows : une erreur de droits ou un service absent déclenchera simplement le bloc catch, ce qui fait partie du résultat attendu.

Nous fiabilisons le script de la séance précédente.

Étape 1 — Forcer l'erreur bloquante


Bonne pratique - Sans Stop, le catch est décoratif

Sans -ErrorAction Stop, le bloc catch n'est jamais atteint : l'erreur reste non bloquante et le script continue silencieusement après la ligne fautive.

Étape 2 — Journaliser le message d'erreur


Bonne pratique - Jamais de catch vide

Un bloc catch qui ne fait rien masque le problème et rend le débogage impossible — le même principe déjà vu avec les exceptions PHP.

Étape 3 — Ajouter un catch typé avant le catch générique


Bonne pratique - Du plus précis au plus général

Placez toujours vos blocs catch typés avant le catch générique : PowerShell évalue les blocs dans l'ordre et s'arrête au premier qui correspond.

Étape 4 — Toujours journaliser la fin du traitement


Bonne pratique - finally pour libérer les ressources

finally garantit qu'une ligne de journal, une fermeture de session ou de fichier s'exécute toujours, même en cas d'échec — indispensable pour ne jamais laisser une ressource ouverte.

📌 Une solution