1. Les conditions
$espaceLibreGo = [math]::Round((Get-PSDrive C).Free / 1GB, 1)
if ($espaceLibreGo -lt 10) {
Write-Warning "Espace critique : $espaceLibreGo Go"
}
elseif ($espaceLibreGo -lt 30) {
Write-Host "Espace à surveiller : $espaceLibreGo Go" -ForegroundColor Yellow
}
else {
Write-Host "Espace suffisant : $espaceLibreGo Go" -ForegroundColor Green
}
- La condition est entre parenthèses, le bloc entre accolades.
- L’accolade ouvrante reste sur la ligne du
if: PowerShell est sensible à cette mise en forme. - Les opérateurs sont ceux du niveau 1 :
-eq,-lt,-like,-match, combinés par-and,-or,-not.
Ce qui est considéré comme vrai
| Valeur | Évaluée comme |
|---|---|
$true, un nombre différent de 0, une chaîne non vide | Vrai |
$false, 0, "", $null, un tableau vide | Faux |
On peut donc écrire if ($services) { ... } pour dire « si la recherche a donné au moins un résultat ».
Le switch
Pour comparer une même valeur à plusieurs cas, switch est plus lisible qu’une cascade de elseif :
$etat = (Get-Service -Name Spooler).Status
switch ($etat) {
"Running" { Write-Host "Le spouleur fonctionne" }
"Stopped" { Write-Host "Le spouleur est arrêté" }
default { Write-Host "État inattendu : $etat" }
}
# switch accepte aussi des motifs et des conditions
switch -Wildcard ($nomFichier) {
"*.log" { "Journal" }
"*.csv" { "Tableur" }
default { "Autre" }
}
2. Répéter sur une collection
Deux écritures coexistent, et la différence compte en administration.
# 1. foreach : on charge tout en mémoire, puis on parcourt
$services = Get-Service
foreach ($service in $services) {
Write-Host "$($service.Name) : $($service.Status)"
}
# 2. ForEach-Object : on traite chaque objet au fil du pipeline
Get-Service | ForEach-Object {
Write-Host "$($_.Name) : $($_.Status)"
}
foreach | ForEach-Object | |
|---|---|---|
| Position | Dans un script | Dans un pipeline |
| Variable | Celle que l’on nomme | $_ (ou $PSItem) |
| Mémoire | Tout est chargé d’abord | Traitement au fil de l’eau |
| Gros volumes | Risque de saturation | Adapté (des milliers d’objets) |
| Vitesse sur petit volume | Plus rapide | Un peu plus lent |
Règle simple : foreach pour quelques centaines d’éléments dans un script, ForEach-Object quand les données arrivent par le pipeline, par exemple 50 000 lignes de journal.
while, do…while, for
# Attendre qu'un service démarre, avec une limite
$essais = 0
while ((Get-Service Spooler).Status -ne "Running" -and $essais -lt 10) {
Start-Sleep -Seconds 2
$essais++
}
# do : le bloc s'exécute au moins une fois
do {
$reponse = Read-Host "Continuer ? (o/n)"
} while ($reponse -notin @("o", "n"))
# for : compteur classique
for ($i = 1; $i -le 5; $i++) {
New-Item -Path "C:\Temp\test$i.txt" -ItemType File -Force | Out-Null
}
3. Interrompre : break et continue
foreach ($ligne in Get-Content "C:\Temp\comptes.txt") {
if ($ligne -eq "") { continue } # ligne vide : on passe à la suivante
if ($ligne -eq "#FIN") { break } # on sort de la boucle
Write-Host "Traitement de $ligne"
}
Dans un ForEach-Object, break et continue ne se comportent pas comme dans foreach : préfère y filtrer en amont avec Where-Object.
4. Traiter un fichier CSV
C’est le scénario d’administration le plus courant : une liste fournie par le secrétariat ou un export, que l’on traite ligne par ligne.
# eleves.csv : Nom;Prenom;Classe
Import-Csv "C:\Temp\eleves.csv" -Delimiter ";" | ForEach-Object {
$identifiant = ($_.Prenom.Substring(0, 1) + $_.Nom).ToLower()
[PSCustomObject]@{
Identifiant = $identifiant
Affichage = "$($_.Prenom) $($_.Nom)"
Classe = $_.Classe
}
} | Export-Csv "C:\Temp\comptes.csv" -Delimiter ";" -Encoding UTF8 -NoTypeInformation
5. Mini-projet : le rangement du dossier Téléchargements
6. Bilan
Exercices corrigés
Les boucles d’administration se testent toujours en deux temps : d’abord une version qui affiche ce qu’elle ferait, ensuite une version qui agit. Les corrigés respectent systématiquement cet ordre.
Exercice 3.1 — Décider
Voir le corrigé
# 1.
if ((Get-Service Spooler).Status -eq "Running") { "Démarré" } else { "Arrêté" }
# 2.
$chemin = Read-Host "Chemin du fichier"
if (Test-Path $chemin) { "Le fichier existe" } else { "Introuvable" }
# 3.
$tailleMo = 42
switch ($tailleMo) {
{ $_ -lt 1 } { "Petit fichier"; break }
{ $_ -le 100 } { "Fichier moyen"; break }
default { "Gros fichier" }
}
# 4.
$estAdmin = (New-Object Security.Principal.WindowsPrincipal(
[Security.Principal.WindowsIdentity]::GetCurrent())
).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if ($estAdmin) { "Console administrateur" } else { "Console utilisateur" }
Explication
Dans un switch, { $_ -lt 1 } est une condition et non une valeur : cela permet de classer des intervalles. Le break empêche l’évaluation des cas suivants.
Exercice 3.2 — Répéter
Voir le corrigé
# 1.
foreach ($s in Get-Service | Where-Object Status -eq "Stopped") {
"$($s.Name) - démarrage : $($s.StartType)"
}
# 2.
New-Item C:\Temp\Essai -ItemType Directory -Force | Out-Null
for ($i = 1; $i -le 5; $i++) {
New-Item "C:\Temp\Essai\fichier$i.txt" -ItemType File -Force | Out-Null
}
# 3.
foreach ($f in Get-ChildItem C:\Temp\Essai -Filter *.txt) {
Get-Date | Out-File $f.FullName -Encoding utf8
"--- $($f.Name) ---"
Get-Content $f.FullName
}
# 4.
$essais = 0
while (-not (Test-Path "C:\Temp\declencheur.txt") -and $essais -lt 15) {
Start-Sleep -Seconds 1
$essais++
}
if (Test-Path "C:\Temp\declencheur.txt") { "Fichier détecté après $essais seconde(s)" }
else { "Délai dépassé" }
Explication
Out-Null sert à ne pas polluer la sortie avec les objets renvoyés par New-Item. La boucle d’attente combine deux conditions : l’état recherché et le nombre d’essais.
Exercice 3.3 — Générer des identifiants
Voir le corrigé
function Remove-Accent {
param([string]$Texte)
($Texte.Normalize([Text.NormalizationForm]::FormD) -replace "\p{Mn}", "")
}
$existants = @{}
Import-Csv "C:\Temp\eleves.csv" -Delimiter ";" | ForEach-Object {
$base = (Remove-Accent ($_.Prenom.Substring(0, 1) + $_.Nom)).ToLower() -replace "[^a-z0-9]", ""
$identifiant = $base
$n = 1
while ($existants.ContainsKey($identifiant)) {
$n++
$identifiant = "$base$n"
}
$existants[$identifiant] = $true
[PSCustomObject]@{
Identifiant = $identifiant
Affichage = "$($_.Prenom) $($_.Nom)"
Classe = $_.Classe
}
} | Format-Table -AutoSize
Explication
La normalisation retire les accents en décomposant les caractères (é devient e + un signe diacritique), puis en supprimant les signes. Le dictionnaire $existants mémorise les identifiants déjà attribués pour gérer les doublons.
Pour aller plus loin
Pense aussi à la longueur maximale : SamAccountName est limité à 20 caractères dans Active Directory. Un Substring de sécurité évitera une erreur au niveau 6.
Mini-projet
Voir le corrigé
$source = "$env:USERPROFILE\Downloads"
$simulation = $true # passer à $false pour déplacer réellement
$categories = "Images", "Documents", "Archives", "Installeurs", "Divers"
foreach ($c in $categories) {
New-Item -Path (Join-Path $source $c) -ItemType Directory -Force | Out-Null
}
$compteur = @{}
Get-ChildItem $source -File | ForEach-Object {
$categorie = switch ($_.Extension.ToLower()) {
{ $_ -in ".jpg", ".jpeg", ".png", ".gif", ".webp" } { "Images"; break }
{ $_ -in ".pdf", ".docx", ".xlsx", ".pptx", ".txt", ".csv" } { "Documents"; break }
{ $_ -in ".zip", ".7z", ".rar", ".tar", ".gz" } { "Archives"; break }
{ $_ -in ".exe", ".msi" } { "Installeurs"; break }
default { "Divers" }
}
"$($_.Name) -> $categorie"
if (-not $simulation) {
Move-Item $_.FullName (Join-Path $source $categorie) -Force
}
$compteur[$categorie] = 1 + $compteur[$categorie]
}
""
"--- Récapitulatif ---"
$compteur.GetEnumerator() | Sort-Object Name | Format-Table Name, Value -AutoSize
Explication
La variable $simulation est le cœur du corrigé : le même code sert à vérifier puis à agir, sans le réécrire. Le compteur en table de hachage s’incrémente même pour une catégorie jamais vue, car $null + 1 vaut 1 en PowerShell.
Pour aller plus loin
Une amélioration utile : en cas de conflit de nom, ajouter un horodatage au fichier déplacé plutôt que de l’écraser.
🧠 Testez vos connaissances
5 questions. Répondez puis cliquez sur « Corriger ». Rien n’est enregistré : c’est de l’auto-évaluation.
© 2026 Bruno Jousseaume. Ce cours est publié sous licence CC BY-NC-SA 4.0 : vous pouvez le partager et l’adapter en citant l’auteur et un lien vers cette page, sans usage commercial, et en partageant vos adaptations sous la même licence.