TP1re année19 livrablesQuiz corrigé
Automatiser de petites tâches
Tu sais taper des commandes une par une. Et si la machine les enchaînait toute seule, à ta place ? Bienvenue dans les scripts — ton premier pas de vrai automaticien.
Par Josselyn C., prof de BTS SIO. Publié le , mis à jour le .
- Objectif
- écrire des scripts PowerShell (et bash) qui enchaînent des commandes, avec variables, questions, conditions, listes et boucles
- On manipule
- PowerShell sur Windows + bash dans ta VM Linux
- Pré-requis
- connaître le terminal (cours « Le terminal ») et avoir une VM Linux qui marche
- Pour qui
- SLAM et SISR, 1re année
- Durée
- environ 6 h
Compétences : C1.5 Mettre à disposition des utilisateurs un service informatique, C2.1 Concevoir et développer une solution applicative
Un script ne fait rien de dangereux : c'est juste une liste de commandes que tu écris et relis.
Avant de commencer
Pourquoi automatiser
Imagine : chaque matin, tu dois copier des fichiers dans un dossier de sauvegarde, daté du jour. À la main, c'est 5 minutes de clics, tous les jours, à se tromper une fois sur deux. Avec un script, tu écris la procédure une seule fois, et elle se rejoue en une seconde, sans erreur, autant de fois que tu veux. C'est le cœur du métier : faire faire à la machine ce qui est répétitif.
Ce que tu sauras faire à la fin
| Brique | Tu sauras… |
|---|---|
| Script | écrire un fichier de commandes et l'exécuter |
| Variable | stocker et réutiliser une valeur (texte ou nombre) |
| Question | demander une info à l'utilisateur |
Condition (if, switch) | faire réagir le script selon les cas |
| Liste | ranger plusieurs valeurs et les parcourir |
| Boucle | répéter une action sur plein de fichiers |
| Fonction | réutiliser un bloc de code nommé |
| Argument | donner des infos au script au lancement |
0. C'est quoi un script ?
Un script est un simple fichier texte qui contient une liste de commandes. Quand tu « lances » ce fichier, l'ordinateur exécute les commandes l'une après l'autre, de haut en bas, comme si tu les avais tapées toi-même — mais d'un coup.
| Mot | Définition |
|---|---|
| Script | fichier texte contenant une suite de commandes |
.ps1 | l'extension d'un script PowerShell (Windows) |
.sh | l'extension d'un script bash (Linux) |
| Exécuter | « lancer » le script pour que ses commandes se jouent |
| Variable | une boîte nommée qui contient une valeur |
Flag A1
Checkpoint
Avec tes mots : quelle est la différence entre taper 5 commandes à la main et les mettre dans un script ? Rends : ta réponse en une à deux phrases.
1. Ton premier script PowerShell
On reste sous Windows pour commencer (tu connais déjà PowerShell). On va créer un fichier .ps1 et le lancer.
Étape 1 — Créer le fichier
Dans ton dossier labo-terminal (ou un nouveau dossier scripts), crée un fichier bonjour.ps1 avec le Bloc-notes, ou en commande :
"Write-Host 'Bonjour, je suis ton premier script !'" | Out-File bonjour.ps1
Étape 2 — Le lancer
.\bonjour.ps1
Bonjour, je suis ton premier script !
Flag B1
Production
Crée et lance bonjour.ps1 qui affiche un message de TON choix. Rends : le contenu de ton script, la commande pour le lancer, et ce qui s'affiche.
2. Construire le script ligne par ligne
Un script à une ligne, c'est peu. On va le faire grandir. Ouvre bonjour.ps1 dans le Bloc-notes (clic droit → Modifier) et écris plusieurs lignes :
Write-Host "=== Sauvegarde GSB ==="
Write-Host "Démarrage..."
Write-Host "Terminé !"
expl.
Trois commandes, chacune sur sa ligne. À l'exécution, elles s'affichent dans l'ordre, de haut en bas. Enregistre, puis relance .\bonjour.ps1.
=== Sauvegarde GSB ===
Démarrage...
Terminé !
Comment ça marche
l'ordre, c'est tout
Un script s'exécute de la première à la dernière ligne, toujours dans cet ordre. Si tu inverses deux lignes, tu inverses deux actions. C'est la base de toute programmation : une suite d'instructions, dans un ordre précis.
Astuce
commenter ton script
Une ligne qui commence par # est un commentaire : la machine l'ignore, c'est une note pour toi (ou pour le collègue qui te relira). Ex. # Ce script sauvegarde les fichiers du jour. Commenter, c'est un réflexe de pro.
Flag B2
Production
Ajoute un commentaire en haut de ton script (qui explique ce qu'il fait) et une 4e ligne d'affichage. Rends : ton script complet et sa sortie.
3. Les variables : mémoriser une valeur
Un script devient utile quand il retient des choses. Une variable, c'est une boîte avec un nom, dans laquelle tu ranges une valeur pour la réutiliser.
$nom = "GSB"
Write-Host "Sauvegarde de l'entreprise $nom"
expl.
$nom — la variable (toujours un $ devant en PowerShell).
= "GSB" — on range le texte « GSB » dedans.
"...$nom..." — dans un texte entre guillemets, PowerShell remplace $nom par sa valeur.
Sauvegarde de l'entreprise GSB
Une variable bien pratique : la date du jour
$date = Get-Date -Format "yyyy-MM-dd"
Write-Host "Nous sommes le $date"
expl.
Get-Date -Format "yyyy-MM-dd" — récupère la date du jour au format 2026-09-21. On la range dans $date pour s'en servir (ex. nommer un dossier de sauvegarde par sa date).
Nous sommes le 2026-09-21
Les types de variables essentiels
PowerShell est dit « à typage dynamique » : il devine le type selon ce que tu mets. Mais tu peux aussi forcer le type pour éviter les surprises.
| Type | Déclaration | Exemple | Piège si absent |
|---|---|---|---|
[string] | [string]$nom = "GSB" | texte | un nombre peut se coller au lieu de s'additionner |
[int] | [int]$age = 18 | entier (−2 milliards à +2 milliards) | une saisie Read-Host reste du texte sans ça |
[double] | [double]$prix = 19.99 | décimal | [int]19.99 → 20 (arrondi !) |
[bool] | [bool]$actif = $true | vrai/faux ($true / $false) | les comparaisons -eq renvoient un bool — tu peux le stocker |
[datetime] | [datetime]$d = "2026-09-01" | date manipulable | Get-Date renvoie un datetime — -Format le transforme en string |
Les variables d'environnement — lire le contexte du système
PowerShell expose les variables système via $env:. Pratique pour personnaliser un script sans rien coder en dur :
Write-Host "Tu es connecté en tant que : $env:USERNAME"
Write-Host "Ton poste s'appelle : $env:COMPUTERNAME"
Write-Host "Ton dossier perso : $env:USERPROFILE"
expl.
$env:USERNAME — le nom de l'utilisateur Windows connecté (ex. marc.dupont).
$env:COMPUTERNAME — le nom réseau du PC (ex. PC-SALLE3-12).
$env:USERPROFILE — le chemin vers le profil utilisateur (ex. C:\Users\marc.dupont). Très utile pour construire un chemin de sauvegarde qui marche sur tous les postes sans connaître le login à l'avance.
Tu es connecté en tant que : marc.dupont
Ton poste s'appelle : PC-SALLE3-12
Ton dossier perso : C:\Users\marc.dupont
Astuce pro
construire un chemin portable
Au lieu de coder C:\Users\marc\bureau (qui ne marche que sur ton poste), utilise :
$bureau = "$env:USERPROFILE\Desktop"
Ce chemin fonctionne sur n'importe quel poste Windows, quel que soit le login. C'est la différence entre un script qui ne marche que chez toi et un script qu'un technicien peut déployer sur 100 machines.
Erreur courante
la variable mal orthographiée
Tu écris $nom à la création mais $non (ou $Nom) plus bas ? PowerShell ne plante pas : il te donne une variable vide, et ton texte affiche un trou. Solution : recopie le nom à l'identique, ou copie-colle-le, pour éviter la faute de frappe.
Flag B3
Production
Crée deux variables : $prenom (le tien) et $date (la date du jour). Affiche : « <prenom> a fait sa sauvegarde le <date> ». Rends : ton script et sa sortie.
4. Texte & nombres : les types et les calculs
Une variable peut contenir deux grandes sortes de valeurs : du texte (« GSB », « Marc ») ou des nombres (18, 3.5). Et elles ne se comportent pas pareil — c'est une notion clé de toute programmation.
Les nombres : on peut calculer
$a = 10
$b = 3
Write-Host "Somme : $($a + $b)"
Write-Host "Produit : $($a * $b)"
expl.
$a = 10 — une variable qui contient le nombre 10 (pas de guillemets → c'est un nombre).
$($a + $b) — le $(...) demande à PowerShell de calculer avant d'afficher. + additionne, * multiplie (- soustrait, / divise).
Somme : 13
Produit : 30
Le texte : on assemble (mais on ne calcule pas)
$prenom = "Marc"
$nom = "Dupont"
Write-Host ($prenom + " " + $nom)
expl.
Avec du texte, + ne fait pas une addition : il colle les morceaux bout à bout (on dit « concaténer »). Résultat : Marc Dupont.
Marc Dupont
Erreur courante
additionner du texte qui ressemble à un nombre
Si une valeur vient de Read-Host (chapitre suivant), c'est toujours du texte, même si tu tapes « 18 ». Alors "18" + 2 donne… 182 (collage !), pas 20. Solution : convertis en nombre avec [int] : [int]$age + 2 donne bien 20.
Guillemets doubles vs guillemets simples — une distinction clé
En PowerShell (et en bash), les guillemets ne font pas la même chose :
| Guillemets | Comportement | Exemple | Résultat |
|---|---|---|---|
"..." doubles | PowerShell interprète les $variables et les $(expressions) | "Bonjour $nom" | Bonjour Marc |
'...' simples | Tout est littéral — aucune substitution, le $ reste tel quel | 'Bonjour $nom' | Bonjour $nom |
$nom = "Marc"
Write-Host "Bonjour $nom" # → Bonjour Marc (interpolation)
Write-Host 'Bonjour $nom' # → Bonjour $nom (littéral)
expl.
Utilise les guillemets simples quand ton texte contient des $ que tu ne veux pas remplacer — typiquement un mot de passe, un chemin avec $ ou un message d'exemple. En bash, la règle est identique : "..." interprète, '...' ne fait rien.
Erreur courante
mot de passe ou regex « mangé »
Tu stockes un mot de passe qui contient $uper$ecret entre guillemets doubles → PowerShell remplace $uper et $ecret par des variables vides → le mot de passe devient vide. Solution : mets les guillemets simples pour tout texte qui contient des $ à conserver tels quels.
Couper, assembler, extraire — les outils texte du pro
| Opération | PowerShell | Exemple + résultat |
|---|---|---|
| Longueur | $nom.Length | "Marc".Length → 4 |
| Majuscules / minuscules | $nom.ToUpper() / .ToLower() | "marc".ToUpper() → MARC |
| Remplacer | $nom.Replace("a","@") | "marc".Replace("a","@") → m@rc |
| Couper en morceaux | $ligne -split "," | "marc,sophie,karim" -split "," → liste de 3 |
| Coller une liste | $liste -join " - " | @("A","B","C") -join " - " → A - B - C |
| Commencer par… | $nom.StartsWith("Ma") | "Marc".StartsWith("Ma") → True |
| Contient… | $nom -like "*ar*" | "Marc" -like "*ar*" → True (* = n'importe quoi) |
Cas concret
lire un CSV simple
Un fichier visiteurs.csv contient Marc,Dupont,Paris sur chaque ligne. Pour extraire le prénom :
$ligne = "Marc,Dupont,Paris"
$champs = $ligne -split ","
Write-Host "Prénom : $($champs[0])" # Marc
Write-Host "Ville : $($champs[2])" # Paris
C'est exactement ce que fait PowerShell quand tu lis un vrai CSV avec Import-Csv — la même logique, automatisée.
Flag B4
Production
Crée $a = 7 et $b = 5. Affiche, sur trois lignes, leur somme, leur produit et leur différence — en t'assurant que ce sont bien des calculs (pas des collages). Rends : ton script et sa sortie.
5. Poser une question
Un script encore plus malin demande une info à celui qui le lance.
$reponse = Read-Host "Quel dossier veux-tu sauvegarder ?"
Write-Host "Tu as choisi : $reponse"
expl.
Read-Host "..." — affiche la question, attend que l'utilisateur tape quelque chose et appuie sur Entrée.
Le texte tapé est rangé dans $reponse, qu'on réutilise ensuite.
Quel dossier veux-tu sauvegarder ?: Documents
Tu as choisi : Documents
Comment ça marche
un script qui dialogue
Avec Read-Host, ton script n'est plus figé : il s'adapte à la réponse. C'est le début de l'interactivité — la même idée que dans une appli qui te demande ton nom puis t'accueille « Bonjour <nom> ».
Rappel
la réponse est du TEXTE
Tout ce que renvoie Read-Host est du texte, même un chiffre. Si tu comptes calculer avec, convertis-le : [int]$age (chapitre 4). On s'en servira dès le chapitre suivant.
Flag B5
Production
Écris un script qui demande le prénom de l'utilisateur, puis affiche un message d'accueil personnalisé. Rends : ton script et un exemple d'exécution.
6. Les conditions : réagir selon les cas
Un script intelligent fait des choix : « SI telle situation, ALORS fais ceci, SINON fais cela ». C'est le if / else.
$dossier = Read-Host "Nom du dossier à sauvegarder"
if (Test-Path $dossier) {
Write-Host "OK, le dossier existe, je sauvegarde."
} else {
Write-Host "Erreur : ce dossier n'existe pas !"
}
expl.
if (Test-Path $dossier) — SI le dossier existe (Test-Path répond vrai)…
{ ... } — …ALORS fais ce qui est entre accolades.
else { ... } — SINON (le dossier n'existe pas), fais l'autre bloc.
Comment ça marche
un test vrai/faux
Le if évalue une question à réponse oui/non (le dossier existe-t-il ?). Selon la réponse, il prend un chemin ou l'autre. C'est la brique qui rend un programme « intelligent » : il ne fait pas toujours la même chose bêtement, il s'adapte.
Comparer deux valeurs : les opérateurs
Le plus souvent, le test du if compare deux valeurs : un nombre est-il plus grand qu'un autre ? deux textes sont-ils égaux ? Surprise : en PowerShell on n'écrit pas > ni ==, mais des mots-clés qui commencent par un tiret.
| Opérateur | Sens | Vrai quand… |
|---|---|---|
-eq | égal à | 5 -eq 5 |
-ne | différent de | 5 -ne 3 |
-gt | plus grand que | 5 -gt 3 |
-ge | plus grand ou égal | 5 -ge 5 |
-lt | plus petit que | 3 -lt 5 |
-le | plus petit ou égal | 3 -le 5 |
$age = Read-Host "Quel age as-tu ?"
if ([int]$age -ge 18) {
Write-Host "Tu es majeur."
} else {
Write-Host "Tu es mineur."
}
expl.
[int]$age — Read-Host renvoie toujours du texte. [int] le transforme en nombre, sinon la comparaison se ferait sur du texte (et « 9 » passerait pour plus grand que « 18 »).
-ge 18 — vrai si le nombre est supérieur ou égal à 18, donc on entre dans le premier bloc.
Quel age as-tu ?: 20
Tu es majeur.
Plusieurs cas : elseif
$note = [int](Read-Host "Ta note sur 20 ?")
if ($note -ge 16) {
Write-Host "Très bien"
} elseif ($note -ge 10) {
Write-Host "Admis"
} else {
Write-Host "À retravailler"
}
expl.
elseif (...) — « SINON, SI cet autre test est vrai… ». PowerShell teste les cas dans l'ordre et s'arrête au premier vrai. Une note de 12 n'est pas ≥ 16, mais elle est ≥ 10 → « Admis ».
Ta note sur 20 ?: 12
Admis
Combiner plusieurs conditions : -and, -or, -not
Parfois un seul test ne suffit pas : « SI l'âge est ≥ 18 ET le pays est France… ». Les opérateurs logiques combinent plusieurs conditions.
| Opérateur | Sens | Vrai quand… |
|---|---|---|
-and | ET — les deux doivent être vrais | ($age -ge 18) -and ($pays -eq "France") |
-or | OU — au moins un doit être vrai | ($ext -eq ".jpg") -or ($ext -eq ".png") |
-not | NON — inverse le résultat | -not (Test-Path $dossier) = dossier absent |
$age = [int](Read-Host "Ton âge ?")
$pass = Read-Host "Ton code (1234) ?"
if ($age -ge 18 -and $pass -eq "1234") {
Write-Host "Accès autorisé."
} elseif ($age -lt 18) {
Write-Host "Accès refusé : tu es mineur."
} else {
Write-Host "Accès refusé : code incorrect."
}
expl.
$age -ge 18 -and $pass -eq "1234" — les DEUX tests doivent être vrais pour entrer dans le premier bloc. PowerShell évalue de gauche à droite et s'arrête dès que le résultat est connu (court-circuit) : si l'âge est < 18, il ne vérifie pas le code.
Ton âge ?: 20
Ton code (1234) ?: 9999
Accès refusé : code incorrect.
Cas concret
filtrer des fichiers par extension ET par taille
$fichiers = Get-ChildItem
foreach ($f in $fichiers) {
if ($f.Extension -eq ".log" -and $f.Length -gt 1MB) {
Write-Host "Gros log à archiver : $($f.Name) ($([int]($f.Length/1MB)) Mo)"
}
}
1MB est une constante PowerShell (= 1 048 576 octets). Il existe aussi 1KB et 1GB. Nettement plus lisible que d'écrire 1048576.
Erreur courante
accolade ou parenthèse oubliée
Message : Accolade fermante « } » manquante… Cause : chaque { doit avoir son }, chaque ( son ). Solution : compte-les, et aligne bien ton if et son else. Un éditeur de code (comme VS Code) les colore pour t'aider.
Flag C1
Debug
Reprends le script de note ci-dessus, lance-le trois fois (une note ≥ 16, une entre 10 et 15, une < 10). Rends : les trois messages obtenus, et explique ce qui décide du message affiché.
7. Le switch : choisir parmi plein de cas
Quand tu dois tester la même variable contre beaucoup de valeurs, enchaîner les elseif devient lourd. Le switch fait ça plus proprement : « regarde cette valeur, et selon ce qu'elle vaut, fais le bon bloc ».
$jour = [int](Read-Host "Numéro du jour (1-7) ?")
switch ($jour) {
1 { Write-Host "Lundi" }
2 { Write-Host "Mardi" }
3 { Write-Host "Mercredi" }
4 { Write-Host "Jeudi" }
5 { Write-Host "Vendredi" }
6 { Write-Host "Samedi" }
7 { Write-Host "Dimanche" }
default { Write-Host "Numéro invalide (attendu 1 à 7)" }
}
expl.
switch ($jour) — « examine la valeur de $jour ».
3 { ... } — si $jour vaut 3, exécute ce bloc. Chaque ligne = un cas possible.
default { ... } — le cas « aucun des précédents » (comme le else du if). Très important pour gérer les saisies imprévues.
Numéro du jour (1-7) ?: 3
Mercredi
Quand préférer switch au if/elseif ?
Dès que tu compares une seule variable à plusieurs valeurs fixes (un menu, un code, une extension de fichier), le switch est plus lisible. Si tes tests sont variés (un nombre ≥ 18 ET un dossier existe…), reste sur if.
Erreur courante
oublier le default
Sans default, si la valeur ne correspond à aucun cas, le switch ne fait… rien (aucun message, ce qui laisse l'utilisateur perplexe). Réflexe de pro : mets toujours un default qui dit clairement « valeur non reconnue ».
Flag B6
Production
Écris un petit menu : demande un numéro (1, 2 ou 3) et, avec un switch, affiche « Sauvegarde », « Restauration » ou « Quitter ». Ajoute un default pour les autres saisies. Rends : ton script et trois exécutions (1, 2, et une valeur invalide).
8. Les listes (tableaux) : ranger plusieurs valeurs
Jusqu'ici, une variable contenait une valeur. Mais souvent tu veux en ranger plusieurs : la liste des visiteurs, des fichiers à traiter… C'est une liste (on dit aussi « tableau »).
Créer une liste et lire ses éléments
$visiteurs = @("Marc", "Sophie", "Karim")
Write-Host "Premier visiteur : $($visiteurs[0])"
Write-Host "Nombre de visiteurs : $($visiteurs.Count)"
expl.
@("Marc", "Sophie", "Karim") — le @( ... ) crée une liste de trois éléments, séparés par des virgules.
$visiteurs[0] — le premier élément. Attention : on compte à partir de 0 ! [0] = Marc, [1] = Sophie, [2] = Karim.
$visiteurs.Count — combien d'éléments la liste contient (ici 3).
Premier visiteur : Marc
Nombre de visiteurs : 3
Ajouter un élément
$visiteurs += "Lina"
Write-Host "Maintenant : $($visiteurs.Count) visiteurs"
expl.
+= — ajoute « Lina » à la fin de la liste. $visiteurs contient désormais 4 noms.
Maintenant : 4 visiteurs
Parcourir toute la liste
foreach ($v in $visiteurs) {
Write-Host "- $v"
}
expl.
foreach ($v in $visiteurs) — « pour chaque élément $v de la liste »… Le bloc se répète une fois par élément. C'est la façon naturelle de traiter une liste sans connaître sa taille à l'avance.
- Marc
- Sophie
- Karim
- Lina
Erreur courante
l'index qui sort de la liste
Une liste de 4 éléments va de [0] à [3]. Écrire $visiteurs[4] ne renvoie… rien (vide), car il n'y a pas de 5e case. Règle : le dernier index = .Count - 1. Astuce : $visiteurs[-1] donne directement le dernier élément.
Flag B7
Production
Crée une liste $courses avec 3 articles. Ajoute-en un 4e avec +=. Puis affiche, avec une boucle foreach, « Article : <nom> » pour chacun, et à la fin le nombre total d'articles. Rends : ton script et sa sortie.
9. Les boucles sur des fichiers
La vraie puissance : faire la même action sur plein d'éléments réels — par exemple tous les fichiers d'un dossier. On combine une liste de fichiers et une boucle.
$fichiers = Get-ChildItem *.txt
foreach ($f in $fichiers) {
Write-Host "Trouvé : $($f.Name)"
}
expl.
$fichiers = Get-ChildItem *.txt — range tous les fichiers .txt dans une variable (c'est une liste, comme au chapitre précédent).
foreach ($f in $fichiers) — « pour chaque fichier $f de la liste »…
{ Write-Host ... } — …répète l'action (ici, afficher son nom). La boucle tourne autant de fois qu'il y a de fichiers.
Trouvé : note.txt
Trouvé : moi.txt
Trouvé : rapport.txt
Comment ça marche
l'usine automatique
Sans boucle, traiter 100 fichiers = écrire 100 lignes. Avec une boucle = 3 lignes, que tu aies 3 ou 3000 fichiers. C'est exactement pour ça qu'on automatise : la boucle ne se fatigue jamais et ne saute aucun élément.
Pourquoi ce $(...) autour de $f.Name ?
Dans un texte entre guillemets, PowerShell remplace une variable simple comme $f sans rien de plus. Mais $f.Name (la propriété « Nom » du fichier) doit d'abord être calculée : on l'entoure de $( ) pour dire « calcule ça, puis colle le résultat dans le texte ». Règle simple : variable seule → $f ; variable suivie d'un point → $($f.Name).
Les propriétés utiles d'un fichier (tableau de référence)
| Propriété | Type | Valeur exemple | Usage type |
|---|---|---|---|
$f.Name | texte | rapport.txt | Afficher, renommer |
$f.FullName | texte | C:\scripts\rapport.txt | Copier, déplacer, supprimer — toujours utiliser FullName, jamais Name seul dans une commande de fichier |
$f.Extension | texte | .txt | Filtrer par type (inclut le point : ".txt", pas "txt") |
$f.Length | entier (octets) | 4096 | Filtrer par taille — divise par 1KB, 1MB ou 1GB pour avoir des unités lisibles |
$f.LastWriteTime | datetime | 2026-09-20 14:32 | Trier, supprimer les anciens (ex. logs de plus de 30 jours) |
$f.CreationTime | datetime | 2026-09-01 09:00 | Dater une archive, tracer la création |
$f.Attributes | flags | Hidden, ReadOnly | Détecter les fichiers cachés ou en lecture seule |
$f.Directory | objet dossier | C:\scripts | Remonter au dossier parent |
Exemple pro — nettoyer les logs de plus de 30 jours
Cas réel : un serveur GSB accumule des journaux d'application dans C:\Logs. Chaque nuit, un script supprime ceux de plus de 30 jours pour éviter de saturer le disque. C'est une des automatisations les plus demandées en entreprise.
# Nettoyage automatique des logs GSB (script pro)
$dossierLogs = "C:\Logs"
$limiteJours = 30
$dateButoir = (Get-Date).AddDays(-$limiteJours)
$supprimés = 0
Get-ChildItem -Path $dossierLogs -Filter *.log -File |
Where-Object { $_.LastWriteTime -lt $dateButoir } |
ForEach-Object {
Remove-Item $_.FullName
$supprimés++
}
Write-Host "$supprimés log(s) supprimé(s) antérieurs au $($dateButoir.ToString('yyyy-MM-dd'))"
expl.
Get-ChildItem -Filter *.log -File — -Filter délègue le filtrage des noms au système de fichiers (OS), ce qui est plus rapide qu'un Where-Object supplémentaire. -File exclut les sous-dossiers de la liste.
| — le pipe : chaque objet (fichier) produit par la commande de gauche est transmis immédiatement à la commande suivante, un par un. La liste complète n'est jamais stockée en mémoire d'un coup — sur 100 000 fichiers, c'est la différence entre 2 secondes et 10 minutes.
Where-Object { $_.LastWriteTime -lt $dateButoir } — $_ est l'objet en cours (le fichier). Ne garde que ceux dont la date de dernière modification est antérieure au seuil. Les fichiers récents ne sont jamais touchés.
Remove-Item $_.FullName — supprime par chemin complet. Sans FullName, si le script est lancé depuis un autre dossier, PowerShell ne trouve pas le fichier.
3 log(s) supprimé(s) antérieurs au 2026-08-03
Teste toujours avec -WhatIf avant de supprimer pour de vrai
Remplace Remove-Item $_.FullName par Remove-Item $_.FullName -WhatIf pour voir la liste de ce qui serait supprimé — sans rien supprimer. Vérifie la liste, puis retire le -WhatIf. Réflexe incontournable avant tout script de nettoyage.
Et si le dossier est vide ou ne contient aucun log ancien ?
Get-ChildItem renvoie une liste vide, Where-Object ne laisse passer aucun fichier, la boucle ne tourne pas : le script affiche « 0 log(s) supprimé(s) ». C'est le comportement attendu, pas une erreur. Tu peux ajouter en fin : if ($supprimés -eq 0) { Write-Host "Rien à nettoyer." }
Flag B8
Production
Écris une boucle qui affiche le nom et la taille de chaque fichier .txt de ton dossier (indice : $($f.Name) et $($f.Length)). Rends : ton script et sa sortie.
10. D'autres boucles : compter et répéter
Le foreach parcourt une liste. Mais parfois on veut compter (de 1 à 10) ou répéter tant qu'une condition est vraie. Deux autres boucles pour ça.
La boucle for : compter
for ($i = 1; $i -le 5; $i++) {
Write-Host "Tour numéro $i"
}
expl.
$i = 1 — point de départ (le compteur démarre à 1).
$i -le 5 — condition pour continuer (« tant que $i est inférieur ou égal à 5 »).
$i++ — à chaque tour, ajoute 1 au compteur.
→ la boucle tourne 5 fois, avec $i qui vaut 1, 2, 3, 4, 5.
Tour numéro 1
Tour numéro 2
Tour numéro 3
Tour numéro 4
Tour numéro 5
La boucle while : tant que…
$compte = 3
while ($compte -gt 0) {
Write-Host "Décollage dans $compte..."
$compte = $compte - 1
}
Write-Host "Décollage !"
expl.
while ($compte -gt 0) — répète tant que $compte est supérieur à 0.
À chaque tour on diminue $compte de 1 ; quand il atteint 0, la boucle s'arrête.
Décollage dans 3...
Décollage dans 2...
Décollage dans 1...
Décollage !
Important
la boucle infinie
Dans un while, il faut toujours que quelque chose fasse avancer vers la fin (ici, $compte qui diminue). Si tu oublies, la boucle tourne à l'infini et fige le terminal. Pour l'arrêter : Ctrl + C. Vérifie toujours ta condition de sortie.
La boucle do…while : exécuter au moins une fois
Avec while, si la condition est fausse dès le départ, le bloc ne s'exécute jamais. do…while garantit un premier passage, puis vérifie la condition :
do {
$reponse = Read-Host "Tape 'ok' pour continuer"
} while ($reponse -ne "ok")
expl.
Le do { ... } while (condition) exécute le bloc, puis teste. Ici, le script redemande tant que l'utilisateur ne tape pas « ok ». C'est le patron classique pour valider une saisie : on pose la question au moins une fois, et on boucle si la réponse est incorrecte.
Tape 'ok' pour continuer: non
Tape 'ok' pour continuer: peut-être
Tape 'ok' pour continuer: ok
(suite du script)
Générer une plage de nombres : 1..10
PowerShell a un raccourci pratique pour créer une suite de nombres sans écrire une boucle for :
# Afficher les nombres de 1 à 5
1..5 | ForEach-Object { Write-Host "Tour $_" }
# Créer 5 dossiers nommés "labo-1" à "labo-5"
1..5 | ForEach-Object { New-Item -ItemType Directory -Name "labo-$_" | Out-Null }
Write-Host "5 dossiers créés."
expl.
1..5 — génère la liste @(1, 2, 3, 4, 5) instantanément (fonctionne aussi à l'envers : 5..1).
| — le « pipe » transmet chaque élément à la commande suivante.
ForEach-Object { ... } — version « pipeline » du foreach. $_ représente l'élément courant (l'équivalent du $f du foreach ($f in ...)).
5 dossiers créés.
Tableau comparatif — quelle boucle choisir ?
| Boucle | Quand l'utiliser | Point clé |
|---|---|---|
foreach ($x in $liste) | Parcourir une liste (fichiers, noms, lignes…) dont la taille est déjà connue | Le plus lisible pour les collections |
for ($i=1; …; $i++) | Compter (de A à B), ou quand tu as besoin du numéro de tour dans le bloc | Utile pour indexer ($liste[$i]) |
while (condition) | Répéter tant qu'une condition extérieure est vraie (téléchargement, attente de réponse) | Peut ne jamais s'exécuter si la condition est fausse d'emblée |
do { } while (condition) | Toujours exécuter au moins une fois (valider une saisie) | La vérification a lieu après le premier passage |
1..N | ForEach-Object | Générer une série rapide sans déclarer de compteur | Lisible pour créer N éléments d'un coup |
Flag B9
Production
Écris une boucle for qui affiche la table de multiplication de 7 (de 7×1 à 7×10). Rends : ton script et sa sortie.
11. Les fonctions : réutiliser un bloc
Quand tu fais plusieurs fois la même chose dans un script, tu ne réécris pas le code à chaque fois : tu en fais une fonction — un bloc nommé, que tu appelles autant que tu veux. C'est une brique majeure de la programmation.
Définir et appeler une fonction
function Saluer {
param($nom)
Write-Host "Bonjour $nom, bienvenue chez GSB !"
}
Saluer "Marc"
Saluer "Sophie"
expl.
function Saluer { ... } — on définit un bloc réutilisable nommé Saluer.
param($nom) — la fonction attend une information : un paramètre (ici, le nom à saluer).
Saluer "Marc" — on appelle la fonction en lui passant « Marc ». On peut l'appeler autant de fois qu'on veut, avec des valeurs différentes.
Bonjour Marc, bienvenue chez GSB !
Bonjour Sophie, bienvenue chez GSB !
Une fonction qui RENVOIE un résultat
function CalculerTTC {
param($prixHT)
return $prixHT * 1.20
}
$total = CalculerTTC 100
Write-Host "Prix TTC : $total euros"
expl.
return ... — la fonction renvoie une valeur (ici le prix TTC calculé).
$total = CalculerTTC 100 — on récupère ce résultat dans une variable pour s'en servir.
Prix TTC : 120 euros
Pourquoi c'est essentiel
« ne te répète pas »
Une fonction écrite une fois est réutilisée partout. Si la règle change (la TVA passe à 21 %), tu corriges un seul endroit, et tout le script est à jour. C'est le principe « DRY » (Don't Repeat Yourself) : on évite de copier-coller du code. Toute la programmation moderne repose là-dessus.
Rendre les paramètres robustes : typage et obligation
Pour les scripts qu'on partage ou qu'on automatise, il faut que la fonction refuse de fonctionner si on lui passe n'importe quoi. PowerShell permet de typer les paramètres et de les rendre obligatoires :
function CalculerTVA {
param(
[Parameter(Mandatory=$true)]
[double]$prixHT,
[double]$tauxTVA = 0.20 # valeur par défaut = 20%
)
return [math]::Round($prixHT * (1 + $tauxTVA), 2)
}
CalculerTVA 100 # → 120.0 (taux par défaut)
CalculerTVA 100 0.055 # → 105.5 (TVA réduite 5,5%)
CalculerTVA # → PowerShell demande le prixHT !
expl.
[Parameter(Mandatory=$true)] — si tu oublies de passer ce paramètre, PowerShell te le demande interactivement plutôt que de planter silencieusement.
[double]$prixHT — force le type. Si tu passes du texte (CalculerTVA "cent"), PowerShell refuse immédiatement avec un message clair : Impossible de convertir la valeur « cent » en type « System.Double ».
[double]$tauxTVA = 0.20 — valeur par défaut : la fonction marche sans qu'on précise le taux, tout en laissant la possibilité de le changer.
[math]::Round(..., 2) — arrondit à 2 décimales (sinon 1.20 * 100 peut donner 119.99999999 à cause de la représentation binaire des flottants).
120
105.5
Fournissez des valeurs pour les paramètres suivants :
prixHT :
Le return implicite — PowerShell est différent de C#
Piège : tout ce qui n'est pas capturé en PowerShell est renvoyé
En PowerShell, une fonction renvoie tout ce qui « tombe » dans le flux de sortie — pas seulement ce qui est après return. Si une commande dans ta fonction affiche quelque chose et que tu ne la captures pas avec | Out-Null ou $var = ..., elle fait partie du résultat.
function Mauvais {
New-Item -ItemType Directory -Name "test" # ← sort un objet dans le flux !
return 42
}
$r = Mauvais # $r contient DEUX valeurs : l'objet dossier + 42
Solution : redirige les sorties parasites vers Out-Null :
New-Item -ItemType Directory -Name "test" | Out-Null
Erreur courante
appeler une fonction avant de l'avoir définie
En PowerShell, une fonction doit être définie plus haut dans le fichier que l'endroit où tu l'appelles. Si tu écris Saluer "Marc" avant le function Saluer { }, tu obtiens Le terme « Saluer » n'est pas reconnu…. Solution : mets tes fonctions en haut du script, les appels en dessous.
Flag B10
Production
Écris une fonction EstMajeur qui prend un âge en paramètre et affiche « majeur » ou « mineur » (avec un if dedans). Appelle-la avec trois âges différents. Rends : ta fonction et les trois résultats.
12. Donner des arguments au script lui-même
Jusqu'ici, soit ton script demandait les infos avec Read-Host, soit elles étaient écrites « en dur ». Mais un vrai outil de pro se lance avec ses infos dès la ligne de commande — comme Copy-Item source destination. Ces infos passées au lancement s'appellent des arguments.
param() en tête de script
Crée salut.ps1 avec, tout en haut (première instruction !) :
param($prenom, $ville)
Write-Host "Salut $prenom, tu viens de $ville !"
expl.
param($prenom, $ville) — déclare les deux infos que le script attend au lancement. C'est le même param() que dans une fonction, mais ici pour le script entier.
Important : param() doit être la toute première ligne de code du fichier (les commentaires # au-dessus sont tolérés).
Lancer le script avec ses arguments
.\salut.ps1 Marc Lyon
expl.
Les valeurs sont prises dans l'ordre : Marc → $prenom, Lyon → $ville. On dit que ce sont des arguments positionnels.
Salut Marc, tu viens de Lyon !
Tu peux aussi nommer les arguments (l'ordre n'importe plus) :
.\salut.ps1 -ville Paris -prenom Sophie
Salut Sophie, tu viens de Paris !
Et si je ne sais pas combien d'arguments ? $args
# compte.ps1
Write-Host "Tu as passé $($args.Count) argument(s)."
foreach ($a in $args) {
Write-Host "- $a"
}
expl.
$args — une liste automatique qui contient tout ce que tu as tapé après le nom du script (tu n'as rien à déclarer). Pratique quand le nombre d'arguments est variable.
PS> .\compte.ps1 pomme poire kiwi
Tu as passé 3 argument(s).
- pomme
- poire
- kiwi
Erreur courante
param() pas en première ligne
Si tu mets une commande avant param(), PowerShell refuse : Le terme « param » n'est pas reconnu… ou ignore tes arguments. Solution : param(...) doit ouvrir le script (sous les commentaires éventuels), rien d'exécutable au-dessus.
Flag B11
Production
Écris infodossier.ps1 qui prend en argument un nom de dossier (param($dossier)), vérifie qu'il existe (Test-Path), et affiche le nombre de fichiers qu'il contient (indice : (Get-ChildItem $dossier).Count). Lance-le ainsi : .\infodossier.ps1 Documents. Rends : ton script et deux exécutions (un dossier qui existe, un qui n'existe pas).
13. Lire et écrire des fichiers dans un script
Un script devient vraiment puissant quand il traite des fichiers : lire une liste, écrire un journal (un « log »). C'est le quotidien de l'administration système.
Lire un fichier ligne par ligne
Crée d'abord un fichier visiteurs.txt avec quelques noms (un par ligne), puis :
$lignes = Get-Content visiteurs.txt
foreach ($ligne in $lignes) {
Write-Host "Visiteur : $ligne"
}
expl.
Get-Content visiteurs.txt — lit le fichier et en fait une liste de lignes (encore une liste, comme au chapitre 8 !).
foreach ($ligne in $lignes) — on traite chaque ligne l'une après l'autre.
Visiteur : Marc
Visiteur : Sophie
Visiteur : Karim
Écrire dans un journal (log) daté
$date = Get-Date -Format "yyyy-MM-dd HH:mm"
"[$date] Script lancé" | Add-Content journal.log
expl.
Add-Content journal.log — ajoute la ligne à la fin du fichier journal (sans effacer ce qu'il contient). À chaque exécution, une nouvelle ligne horodatée s'empile : tu gardes une trace de tout.
Les 4 cmdlets de fichiers — tableau complet
| Cmdlet | Ce qu'il fait | Équivalent bash | Piège à éviter |
|---|---|---|---|
Get-Content fichier | Lit le fichier → liste de lignes | cat fichier | Lire un fichier de 500 Mo charge tout en RAM — préfère Get-Content -ReadCount 100 pour lire par lots |
Set-Content fichier | Écrase le fichier avec le nouveau contenu | echo "..." > fichier | Détruit tout l'historique — jamais sur un log |
Add-Content fichier | Ajoute à la fin (le fichier grandit) | echo "..." >> fichier | Sans -Encoding UTF8, peut écrire en UTF-16 — lisible sous Windows mais illisible sous Linux |
Out-File fichier | Redirige la sortie d'une commande vers un fichier | commande > fichier | Par défaut, Out-File écrase — ajoute -Append pour cumuler |
L'encodage : le piège silencieux des logs
Par défaut, Add-Content écrit en UTF-16 Little Endian (2 octets par caractère, avec BOM). Si tu ouvres ce fichier sous Linux ou dans un éditeur web, les caractères accentués apparaissent comme des symboles é. Force l'encodage UTF-8 sans BOM :
"[$date] Script lancé" | Add-Content journal.log -Encoding UTF8
Et pour lire sans problème d'accent : Get-Content journal.log -Encoding UTF8. Règle pro : toujours préciser l'encodage dès que le fichier peut être lu sur un autre OS.
À quoi ça sert vraiment
Les vrais serveurs écrivent en permanence des logs (« qui s'est connecté, quand, quoi a planté »). Savoir lire un fichier et en écrire un, c'est la base pour automatiser des rapports, traiter des listes d'utilisateurs, ou tracer ce qu'un script a fait.
Lire un CSV structuré : Import-Csv
Un fichier CSV a des colonnes nommées. Get-Content renvoie des lignes brutes de texte que tu dois découper à la main avec -split. Import-Csv fait ça automatiquement et renvoie des objets dotés de propriétés nommées selon l'en-tête — le gain de temps est énorme.
Crée le fichier visiteurs.csv (Bloc-notes, colle ces lignes, enregistre) :
Prenom,Nom,Service
Marc,Dupont,Informatique
Sophie,Martin,Comptabilité
Karim,Benali,Direction
Lis-le avec Import-Csv :
$visiteurs = Import-Csv visiteurs.csv
foreach ($v in $visiteurs) {
Write-Host "$($v.Prenom) $($v.Nom) — service : $($v.Service)"
}
expl.
Import-Csv visiteurs.csv — lit le fichier, utilise la 1re ligne comme noms de colonnes, et renvoie une liste d'objets. Chaque objet a des propriétés .Prenom, .Nom, .Service — exactement les noms de tes colonnes.
$v.Prenom — accès direct à la colonne Prenom, sans découper la ligne manuellement. C'est la différence fondamentale avec Get-Content : tu travailles avec des données, pas avec du texte brut.
Marc Dupont — service : Informatique
Sophie Martin — service : Comptabilité
Karim Benali — service : Direction
Get-Content vs Import-Csv — quand choisir quoi ?
Get-Content | Import-Csv | |
|---|---|---|
| Format du fichier | N'importe quel texte (log, config, liste brute) | CSV avec en-tête (colonnes nommées) |
| Ce qu'on récupère | Liste de chaînes de texte, une par ligne | Liste d'objets avec propriétés nommées |
| Accès à un champ | Découpe manuelle : $l -split "," puis [0], [1]… | Direct : $v.Prenom, $v.Service |
| Séparateur | Tu gères toi-même | Virgule par défaut ; -Delimiter ";" pour les CSV Excel France |
| Cas d'usage typique | Lire un log, traiter un fichier texte non structuré | Importer une liste d'utilisateurs, de postes, de visiteurs |
Erreur courante
CSV généré par Excel en France (séparateur point-virgule)
En France, Excel exporte en CSV avec des points-virgules (;), pas des virgules — parce que la virgule est le séparateur décimal français. Résultat : Import-Csv renvoie une seule colonne avec tout le contenu collé.
Solution : Import-Csv visiteurs.csv -Delimiter ";"
Diagnostic rapide : ouvre le CSV dans le Bloc-notes. Si tu vois des ; entre les champs, utilise -Delimiter ";".
Cas concret GSB
registre d'accueil automatique
L'assistante de direction te fournit un visiteurs.csv chaque matin (exporté depuis le logiciel de réservation). Ton script lit le CSV et génère un log horodaté pour chaque visiteur :
$visiteurs = Import-Csv visiteurs.csv -Delimiter ";"
foreach ($v in $visiteurs) {
$date = Get-Date -Format "yyyy-MM-dd HH:mm"
$ligne = "[$date] BADGE : $($v.Prenom) $($v.Nom) → $($v.Service)"
$ligne | Add-Content registre.log -Encoding UTF8
Write-Host $ligne
}
Ce script tourne en 2 secondes pour 100 visiteurs. À la main, ce serait 30 minutes de copier-coller et de fautes de frappe.
Flag B12
Production
Crée un fichier taches.txt (3-4 tâches, une par ligne). Écris un script qui lit le fichier et affiche « À faire : <tâche> » pour chacune, en numérotant (indice : un compteur $n que tu incrémentes). Rends : ton script et sa sortie.
14. Quand ça plante : lire l'erreur & déboguer
Tous les scripts plantent un jour. Ce qui fait le bon technicien, ce n'est pas de ne jamais se tromper : c'est de savoir lire l'erreur et la corriger calmement.
Décoder un message d'erreur
Set-Location : Impossible de trouver le chemin « C:\labo\absent »...
Au caractère C:\scripts\sauvegarde.ps1:6 : 5
expl.
Un message rouge donne deux choses précieuses : ce qui ne va pas (« chemin introuvable ») et où (sauvegarde.ps1:6 = fichier, ligne 6). Va directement à cette ligne : l'erreur y est, ou juste avant.
La technique du « jalon » (tracer)
Quand un script fait un truc bizarre sans planter, sème des repères pour voir jusqu'où il va :
Write-Host "1 - avant la copie"
Copy-Item "$source\*" $destination -Recurse
Write-Host "2 - après la copie"
expl.
Si tu vois « 1 » mais jamais « 2 », tu sais que ça coince sur la ligne de copie. C'est la méthode la plus simple et la plus efficace pour localiser un problème.
Attraper une erreur proprement : try / catch
try {
Copy-Item "$source\*" $destination -Recurse
Write-Host "Sauvegarde réussie."
} catch {
Write-Host "Échec : $($_.Exception.Message)"
}
expl.
try { ... } — « essaie de faire ça ».
catch { ... } — « si ça rate, fais plutôt ça » (au lieu de planter brutalement, on affiche un message clair). $_.Exception.Message = le détail de l'erreur.
Les 4 erreurs qui reviennent toujours
① une accolade/parenthèse non fermée → « } manquante » ; ② un nom de variable mal orthographié → la variable est vide ; ③ un chemin/fichier qui n'existe pas → « introuvable » ; ④ ExecutionPolicy qui bloque (chapitre 1). Relis l'erreur : elle dit presque toujours laquelle.
Contrôler le comportement sur erreur : $ErrorActionPreference
Par défaut, PowerShell affiche l'erreur en rouge mais continue l'exécution. Dans un script automatisé, c'est dangereux : si la copie rate, le script continue et « confirme » une sauvegarde qui n'a pas eu lieu.
# En haut du script, force l'arrêt immédiat sur toute erreur :
$ErrorActionPreference = "Stop"
try {
Copy-Item "$source\*" $destination -Recurse
Write-Host "Sauvegarde réussie."
} catch {
Write-Host "ÉCHEC : $($_.Exception.Message)"
exit 1 # code de retour 1 = erreur (utile pour les tâches planifiées)
}
expl.
$ErrorActionPreference = "Stop" — transforme toute erreur en exception attrapable par catch. Sans ça, certaines erreurs (chemin introuvable, droits insuffisants) passent en rouge mais n'entrent pas dans le catch.
Les valeurs possibles : "Continue" (défaut — affiche et continue), "Stop" (arrête ou entre dans catch), "SilentlyContinue" (ignore silencieusement — utile pour tester si un fichier ou service existe sans afficher d'erreur), "Inquire" (pose la question à l'utilisateur).
exit 1 — le script quitte avec un code d'erreur. Une tâche planifiée (Gestionnaire de tâches Windows) interprète ce code : 0 = succès, toute autre valeur = échec. C'est la norme Unix/Windows pour tous les outils en ligne de commande.
Tester sans casser : -WhatIf
Beaucoup de cmdlets « destructifs » (Move-Item, Remove-Item, Copy-Item, Set-Content…) acceptent -WhatIf : ils simulent l'action et disent ce qu'ils auraient fait, sans rien toucher.
Remove-Item "*.log" -WhatIf
WhatIf : Opération « Suppression de l'élément » en cours sur la cible « C:\scripts\debug.log ».
WhatIf : Opération « Suppression de l'élément » en cours sur la cible « C:\scripts\journal.log ».
expl.
Tu vois exactement ce qui serait supprimé — sans que rien ne soit supprimé. Réflexe de pro avant tout script de nettoyage : lance-le avec -WhatIf une première fois, vérifie la liste, puis relance sans.
Tracer ce que fait un script : Write-Verbose
$VerbosePreference = "Continue" # active les messages verbose
function SauvegarderDossier {
param([string]$source)
Write-Verbose "Vérification du dossier source : $source"
if (-not (Test-Path $source)) {
Write-Error "Dossier introuvable : $source"
return
}
Write-Verbose "Démarrage de la copie..."
Copy-Item $source "backup-$(Get-Date -Format 'yyyyMMdd')" -Recurse
Write-Verbose "Copie terminée."
Write-Host "Sauvegarde réussie."
}
expl.
Write-Verbose — affiche un message en bleu préfixé de VERBOSE:, mais seulement si $VerbosePreference = "Continue". En production on le laisse à "SilentlyContinue" (silencieux), et on l'active quand on débogue. C'est l'équivalent d'un mode « log détaillé » sans modifier le script.
Write-Error — écrit en rouge dans le flux d'erreurs (sans planter le script). Différent de Write-Host en rouge : l'erreur peut être capturée par le caller du script.
Connaître son environnement : $PSVersionTable
Avant de déboguer, sache dans quelle version de PowerShell tu travailles. Les comportements varient entre PS 5.1 (intégré à Windows) et PS 7+ (à installer séparément) :
$PSVersionTable
Name Value
---- -----
PSVersion 5.1.19041.4291
PSEdition Desktop
BuildVersion 10.0.19041.4291
CLRVersion 4.0.30319.42000
expl.
PSVersion — la version exacte. 5.1.x = Windows PowerShell (intégré, jamais mis à jour). 7.x = PowerShell Core (multiplateforme, maintenu activement, encodage UTF-8 par défaut). En cours on utilise 5.1 ; en entreprise les deux cohabitent souvent.
| Critère | Windows PowerShell 5.1 | PowerShell 7.x |
|---|---|---|
| Présence sur Windows | Intégré (toujours là) | À installer séparément |
Encodage Add-Content | UTF-16 par défaut (pose problème côté Linux) | UTF-8 par défaut (compatible partout) |
| Opérateur ternaire | Non disponible | $x = condition ? "oui" : "non" |
| Modules AD/Exchange | Disponibles nativement | Non disponibles sans passerelle |
| Support actif | Maintenance sécu. uniquement | Développement actif (nouvelles fonctions) |
Mode trace — suivre l'exécution ligne par ligne
Quand un script se comporte de façon inattendue et que tu ne sais pas quelle ligne est en cause, active le mode trace :
Set-PSDebug -Trace 1 # active : chaque ligne est affichée avant exécution
.\monscript.ps1
Set-PSDebug -Off # désactive : revient au mode normal
DEBUG: 1+ >>>> $source = Read-Host "Quel dossier ?"
DEBUG: 2+ >>>> if (Test-Path $source) {
DEBUG: 3+ >>>> Copy-Item "$source\*" $destination -Recurse
expl.
-Trace 1 — affiche les numéros de ligne et les commandes juste avant leur exécution. Arrête le script avec Ctrl+C à la ligne qui bloque.
-Trace 2 — affiche en plus les affectations de variables (encore plus verbeux, utile pour tracer des valeurs inattendues).
Pense à désactiver avec Set-PSDebug -Off une fois le bug trouvé, sinon tout ton terminal continue d'afficher des DEBUG:.
Erreur courante
confondre $? et $LASTEXITCODE
Deux variables tracent le succès d'une commande, et elles ne mesurent pas la même chose :
• $? — True ou False selon que la dernière cmdlet PowerShell a réussi.
• $LASTEXITCODE — le code de retour numérique (0 = succès, autre valeur = erreur) de la dernière commande externe (un programme .exe, git, python, robocopy…).
Piège fréquent : git push échoue → $? peut rester True (PS a bien lancé git) mais $LASTEXITCODE vaut 128 (git a échoué). Toujours vérifier $LASTEXITCODE -ne 0 après une commande externe.
Flag C2
Debug
Prends un de tes scripts, introduis volontairement une faute (ex. enlève une accolade fermante, ou écris mal un nom de variable). Rends : le message d'erreur exact obtenu, la ligne indiquée, et la correction qui répare.
15. Un vrai script utile : la sauvegarde datée
On assemble tout pour résoudre le problème du début : sauvegarder un dossier dans une copie datée du jour. Voici le script complet, commenté.
# Sauvegarde datée d'un dossier — GSB
$source = Read-Host "Quel dossier sauvegarder ?"
if (Test-Path $source) {
$date = Get-Date -Format "yyyy-MM-dd"
$destination = "sauvegarde-$date"
New-Item -ItemType Directory -Name $destination | Out-Null
Copy-Item "$source\*" $destination -Recurse
Write-Host "Sauvegarde de '$source' faite dans '$destination'."
} else {
Write-Host "Le dossier '$source' n'existe pas. Abandon."
}
expl.
Ligne par ligne : on demande le dossier (Read-Host) → on vérifie qu'il existe (if/Test-Path) → on fabrique un nom de dossier avec la date → on le crée → on copie tout dedans → on confirme. Variables, condition, et toutes les commandes des cours précédents, réunies.
| Out-Null — sans ça, New-Item afficherait à l'écran un tableau technique du dossier créé (déroutant). Out-Null avale cet affichage : la sortie reste propre.
"$source\*" — l'\* veut dire « tout ce qu'il y a dans le dossier source » ; -Recurse descend aussi dans les sous-dossiers.
Tu viens d'écrire un outil de pro
Ce type de script tourne sur de vrais serveurs pour sauvegarder des données chaque nuit. Tu as automatisé une tâche réelle de technicien — et compris les briques (variable, condition) qu'on retrouve dans tous les langages.
Version robuste — ce qu'on met en production
La version ci-dessus est pédagogique. Un script qui tourne seul toutes les nuits sans que personne le surveille doit gérer les pannes, journaliser ses actions et signaler les erreurs. Voici ce que devient ce script en conditions réelles :
# sauvegarde-robuste.ps1 — GSB production
param(
[Parameter(Mandatory=$true)]
[string]$source,
[string]$racine = "C:\Sauvegardes"
)
$ErrorActionPreference = "Stop"
$date = Get-Date -Format "yyyy-MM-dd_HHmm"
$destination = Join-Path $racine "sauvegarde-$date"
$log = Join-Path $racine "sauvegarde.log"
function Journaliser {
param([string]$msg)
$h = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
"[$h] $msg" | Add-Content $log -Encoding UTF8
Write-Host $msg
}
try {
if (-not (Test-Path $source)) {
throw "Dossier source introuvable : $source"
}
New-Item -ItemType Directory -Path $destination | Out-Null
Copy-Item "$source\*" $destination -Recurse
$nbFichiers = (Get-ChildItem $destination -Recurse -File).Count
Journaliser "SUCCES — $nbFichiers fichier(s) dans $destination"
exit 0
}
catch {
Journaliser "ECHEC — $($_.Exception.Message)"
exit 1
}
expl.
param([Parameter(Mandatory=$true)] [string]$source, [string]$racine = "C:\Sauvegardes") — le script prend deux arguments : le dossier source (obligatoire) et la racine des sauvegardes (optionnelle, avec valeur par défaut). Exemple : .\sauvegarde-robuste.ps1 -source Documents.
$ErrorActionPreference = "Stop" — transforme toute erreur en exception attrapée par catch. Sans ça, un chemin introuvable ou un défaut de droits s'affiche en rouge mais le script continue — il peut alors « confirmer » une sauvegarde qui n'a pas eu lieu. C'est le pire bug d'un outil de prod.
Join-Path $racine "sauvegarde-$date" — construit le chemin correctement quel que soit l'OS (gère les barres obliques). Plus robuste que la concaténation manuelle "$racine\sauvegarde-$date".
throw "..." — lève manuellement une erreur avec un message lisible, qui entre dans le catch comme n'importe quelle autre erreur.
$nbFichiers = (Get-ChildItem $destination -Recurse -File).Count — vérifie que des fichiers ont réellement été copiés. Copy-Item sur un dossier vide réussit sans rien copier : ce comptage détecte ce cas silencieux.
exit 0 / exit 1 — le code de retour. Le Gestionnaire de tâches Windows (et tout orchestrateur CI/CD) lit ce code : 0 = succès, toute autre valeur = alerter l'administrateur. C'est la norme universelle Unix/Windows.
PS> .\sauvegarde-robuste.ps1 -source Documents
[2026-09-21 22:00:03] SUCCES — 47 fichier(s) dans C:\Sauvegardes\sauvegarde-2026-09-21_2200
Ce que la version production apporte par rapport à la version simple
| Aspect | Version simple (ci-dessus) | Version production |
|---|---|---|
| Arguments | Read-Host (interactif — bloque une tâche planifiée) | param() — lançable sans intervention humaine |
| Erreur | Affichée en rouge, script continue quand même | try/catch → log + exit 1 |
| Journal | Aucune trace | Fichier .log daté à chaque exécution |
| Vérification résultat | Aucune | Compte les fichiers copiés réellement |
| Chemin | Relatif (peut pointer ailleurs selon d'où on lance) | Join-Path (portable et absolu) |
| Code de retour | Aucun (orchestrateur ne sait pas si ça a marché) | exit 0/1 lisible par Task Scheduler et CI/CD |
Flag B13
Production
Crée et teste ce script de sauvegarde sur un de tes dossiers (ex. brouillons). Rends : le nom du dossier de sauvegarde créé (avec la date) et la preuve (ls) que les fichiers ont bien été copiés.
16. Le même esprit côté Linux (bash)
Tout ce que tu viens d'apprendre existe aussi en bash, avec une syntaxe un peu différente. Apprendre l'un te donne 80 % de l'autre. Dans ta VM Linux, crée bonjour.sh :
#!/bin/bash
nom="GSB"
echo "Bonjour depuis le script $nom"
expl.
#!/bin/bash — le « shebang » : la 1re ligne qui dit « ce fichier est un script bash ».
nom="GSB" — une variable (en bash, pas de $ pour la créer, mais un $ pour la lire). Attention : pas d'espace autour du =.
echo "...$nom" — affiche un texte (l'équivalent de Write-Host).
Le rendre exécutable, puis le lancer
chmod +x bonjour.sh
./bonjour.sh
expl.
chmod +x — donne au fichier le droit d'être exécuté (souviens-toi du x des permissions, vu au cours « deux mondes »).
./bonjour.sh — le lance (le ./ = « ici », comme le .\ de PowerShell).
Bonjour depuis le script GSB
Erreur courante (bash)
un espace autour du =
Tu écris nom = "GSB" (avec des espaces) ? bash croit alors que nom est une commande à lancer.
Message : nom : commande introuvable (ou nom: command not found).
Cause : en bash, l'affectation se colle — aucun espace avant ni après le =.
Solution : écris nom="GSB", tout collé. (PowerShell, lui, tolère les espaces : $nom = "GSB". C'est la principale différence à retenir entre les deux.)
Demander une info : read
#!/bin/bash
read -p "Ton prénom ? " prenom
echo "Bonjour $prenom !"
expl.
read -p "..." prenom — affiche la question (-p = « prompt »), attend la saisie, et la range dans la variable prenom (l'équivalent de Read-Host).
Ton prénom ? Marc
Bonjour Marc !
La condition en bash : if … then … fi
#!/bin/bash
read -p "Ton âge ? " age
if [ "$age" -ge 18 ]; then
echo "Tu es majeur."
else
echo "Tu es mineur."
fi
expl.
if [ "$age" -ge 18 ]; then — le test est entre crochets, avec des espaces obligatoires à l'intérieur. -ge = « plus grand ou égal » (mêmes opérateurs que PowerShell pour les nombres : -eq -ne -gt -ge -lt -le).
then ... else ... fi — pas d'accolades en bash : le bloc va de then à fi (« if » écrit à l'envers ferme le bloc).
Ton âge ? 20
Tu es majeur.
Erreur courante (bash)
les pièges du test [ ]
① Espaces obligatoires : [$age -ge 18] plante → il faut [ "$age" -ge 18 ].
② Nombres vs texte : pour comparer des nombres, on utilise -eq -ne -gt -ge -lt -le ; pour comparer du texte, on utilise = et != (ex. [ "$nom" = "Marc" ]). Mélanger les deux donne des résultats faux.
③ Guillemets : entoure toujours la variable de "$age" ; sinon, si elle est vide, le test casse.
La boucle for en bash
#!/bin/bash
for fichier in *.txt; do
echo "Trouvé : $fichier"
done
expl.
for fichier in *.txt; do ... done — « pour chaque .txt du dossier ». Le bloc va de do à done. $fichier prend tour à tour chaque nom.
Trouvé : note.txt
Trouvé : rapport.txt
Une fonction en bash
#!/bin/bash
saluer() {
echo "Bonjour $1, bienvenue chez GSB !"
}
saluer "Marc"
saluer "Sophie"
expl.
saluer() { ... } — définit une fonction nommée saluer.
$1 — le premier argument passé à la fonction (en bash, les paramètres sont $1, $2, $3… au lieu d'être nommés). $1 vaut « Marc » au premier appel, « Sophie » au second.
Bonjour Marc, bienvenue chez GSB !
Bonjour Sophie, bienvenue chez GSB !
Erreur courante (bash)
« Permission denied » ou « bad interpreter »
Message : bash: ./bonjour.sh: Permission denied → tu as oublié le chmod +x bonjour.sh : le fichier n'a pas encore le droit d'être lancé.
Message : bad interpreter: No such file or directory → ton fichier a été créé sous Windows et garde des fins de ligne Windows. Recrée-le dans la VM avec nano bonjour.sh, ou convertis-le (dos2unix bonjour.sh).
Les variables spéciales bash — ce que le script sait sur lui-même
#!/bin/bash
echo "Nom du script : $0"
echo "Nbre d'arguments : $#"
echo "Tous les args : $@"
echo "1er argument : $1"
echo "Code retour préc : $?"
expl.
$0 — nom du script lui-même (ex. ./bonjour.sh). Utile pour les messages d'erreur auto-documentés.
$# — nombre d'arguments passés. Permet de vérifier qu'on a reçu le bon nombre avant de continuer.
$@ — tous les arguments sous forme de liste (équivalent de $args en PowerShell).
$1, $2, $3… — les arguments dans l'ordre (max $9 sans accolades ; ${10} pour le 10e et au-delà).
$? — le code de retour de la dernière commande : 0 = succès, 1 ou plus = erreur. C'est comme exit en PowerShell — toutes les commandes Unix renvoient ça, et on peut tester :
#!/bin/bash
cp "$1" "$2"
if [ $? -ne 0 ]; then
echo "ERREUR : la copie a échoué."
exit 1
fi
echo "Copie réussie."
ERREUR : la copie a échoué.
Vérifier le nombre d'arguments — réflexe de robustesse
#!/bin/bash
# sauvegarde.sh source destination
if [ $# -ne 2 ]; then
echo "Usage : $0 "
exit 1
fi
cp -r "$1" "$2"
echo "Copié : $1 → $2"
expl.
Ce pattern est universel en bash. Si l'utilisateur oublie un argument, il voit exactement comment utiliser le script (grâce à $0 qui rappelle le nom), plutôt qu'un message d'erreur cryptique de cp.
$ ./sauvegarde.sh documents
Usage : ./sauvegarde.sh <source> <destination>
Tableau comparatif complet — PowerShell vs bash
| Notion | PowerShell | bash | Différence clé |
|---|---|---|---|
| Shebang | (aucun — l'extension .ps1 suffit) | #!/bin/bash en ligne 1 | bash : obligatoire ; PowerShell : superflu |
| Rendre exécutable | Set-ExecutionPolicy (une fois) | chmod +x script.sh (par fichier) | PS : politique globale ; bash : droit fichier |
| Lancer | .\script.ps1 | ./script.sh | Backslash vs slash (Windows vs Linux) |
| Variable — créer | $nom = "val" | nom="val" (sans espaces ni $) | bash : $ seulement à la LECTURE, pas à l'écriture |
| Variable — lire | $nom | $nom | Identique |
| Afficher | Write-Host "..." | echo "..." | Identique dans le comportement |
| Demander | Read-Host "?" | read -p "?" var | bash : le nom de la variable est l'argument de read |
| Condition | if (...) { } else { } | if [ ... ]; then ... else ... fi | bash : crochets + espaces obligatoires + fi |
| Opérateurs num. | -eq -ne -gt -ge -lt -le | -eq -ne -gt -ge -lt -le | Identiques ! |
| Opérateurs texte | -eq -ne (insensible casse) | = != (dans les crochets) | bash : guillemets autour de "$var" obligatoires |
| Boucle liste | foreach ($f in $l) { } | for f in liste; do ... done | bash : do/done au lieu des accolades |
| Boucle compteur | for ($i=1; $i -le 5; $i++) { } | for i in {1..5}; do ... done | bash : plage {1..5} très compacte |
| Fonction — déf. | function F { param($a) } | f() { local a=$1 } | bash : pas de param(), tout dans $1 $2… |
| Arguments script | param($a, $b) ou $args | $1 $2 … $# $@ $0 | bash : variables auto sans déclaration |
| Code retour | exit 0 / exit 1 | exit 0 / exit 1 + $? | bash : $? disponible après chaque commande |
| Guillemets | "..." = interprète, '...' = littéral | "..." = interprète, '...' = littéral | Identiques ! |
| Lire un fichier | Get-Content fichier | cat fichier ou while IFS= read -r ligne | bash : boucle while read pour les gros fichiers |
| Écrire (ajouter) | Add-Content fichier -Encoding UTF8 | echo "..." >> fichier | bash : toujours UTF-8, pas de problème d'encodage |
Comment ça marche
même logique, autre accent
PowerShell et bash partagent les mêmes idées (variables, conditions, boucles, fonctions) ; seule l'écriture change un peu (accolades vs then/fi, $nom vs $1). C'est rassurant : tu n'apprends pas dix langages, tu apprends une logique.
Flag D1
Investigation
En bash, on teste l'existence d'un dossier avec -d (indice : if [ -d "$dossier" ]; then ... fi). Cherche aussi le test -f (existence d'un fichier). Rends : ce que vérifient -d et -f, et une phrase sur leur différence.
Flag B14
Production (bash)
Écris accueil.sh qui : demande un prénom (read), puis avec un if affiche « Salut le chef ! » si le prénom est « Marc », sinon « Bienvenue <prénom> ». Termine par une boucle for qui affiche les noms de tous les .sh du dossier. Rends : ton script, la commande chmod, et deux exécutions (avec « Marc » et avec un autre prénom).
17. Mini-projet : ton assistant de rangement
Mission : un script PowerShell qui range automatiquement les fichiers d'un dossier par type, et fait un petit rapport. Tu vas réunir presque toutes les briques : argument/question, condition, switch, liste, boucle, compteur.
Le cahier des charges
- Demande à l'utilisateur le dossier à ranger (Read-Host) — ou prends-le en argument (param).
- Vérifie qu'il existe (if/Test-Path), sinon arrête avec un message.
- Crée (s'ils n'existent pas) trois sous-dossiers :
textes,images,autres. - Parcourt les fichiers (foreach) et déplace chacun selon son extension (
.txt→ textes,.jpg/.png→ images, le reste → autres). - Affiche combien de fichiers ont été rangés.
Indices (lis-les, ils t'évitent les pièges)
Lister seulement les fichiers (et pas les sous-dossiers textes/images/autres que tu viens de créer !) : Get-ChildItem -File. Sans le -File, ta boucle essaierait de déplacer tes propres dossiers.
Extension d'un fichier : $f.Extension (donne .txt, .jpg…).
Tester l'extension : un switch ($f.Extension) est parfait ici — un cas ".txt", un cas ".jpg", etc., et un default pour « autres ».
Déplacer : Move-Item $f.FullName "images" ($f.FullName = le chemin complet du fichier).
Compter : crée $total = 0 avant la boucle, puis $total++ à chaque fichier rangé ; affiche $total à la fin.
Et si je veux d'abord essayer sans rien casser ?
Travaille sur un dossier de test que tu remplis exprès (crée quelques .txt, .jpg, .log bidons). Tant que ton script n'est pas au point, Move-Item déplace pour de vrai : un dossier jetable t'évite de mélanger tes vrais fichiers. Tu peux aussi remplacer temporairement Move-Item par Write-Host "je déplacerais $($f.Name)" pour vérifier la logique avant de déplacer réellement.
Flag B15
Mini-projet (production)
Écris et teste ton assistant de rangement sur un dossier rempli de fichiers variés. Rends : ton script complet et le ls -Recurse final qui montre les fichiers rangés par type + le total affiché.
Défis bonus
Bonus A : ajoute un cas pour les .pdf (un dossier documents) dans ton switch.
Bonus B : à la fin, écris le rapport dans un fichier rangement.log daté (avec Add-Content), au lieu de juste l'afficher.
Bonus C : transforme le test d'extension en une fonction RangerFichier qui prend le fichier en paramètre — pour t'entraîner au « DRY » du chapitre 11.
18. Planifier l'exécution automatique
Écrire un script, c'est bien. Le faire tourner tout seul, sans intervention humaine, à heure fixe chaque nuit, c'est le vrai objectif de l'automatisation. Deux outils selon l'OS : le Gestionnaire de tâches (Windows) et cron (Linux).
Windows — Gestionnaire de tâches (Task Scheduler)
Le Gestionnaire de tâches (taskschd.msc en interface graphique) permet de lancer un programme à une heure précise, au démarrage, ou à intervalle fixe. En PowerShell, on le configure entièrement en ligne de commande — idéal pour l'automatiser sur 100 postes d'un coup.
# Planifier la sauvegarde GSB chaque nuit à 22h
$action = New-ScheduledTaskAction `
-Execute "powershell.exe" `
-Argument "-NonInteractive -File C:\scripts\sauvegarde-robuste.ps1 -source C:\Users\Public\Documents"
$trigger = New-ScheduledTaskTrigger -Daily -At "22:00"
$settings = New-ScheduledTaskSettingsSet `
-ExecutionTimeLimit (New-TimeSpan -Hours 1) `
-StartWhenAvailable
Register-ScheduledTask `
-TaskName "GSB-Sauvegarde-Nuit" `
-Action $action `
-Trigger $trigger `
-Settings $settings `
-RunLevel Highest `
-Force
expl.
-Execute "powershell.exe" -Argument "-NonInteractive -File C:\scripts\..." — -NonInteractive empêche PowerShell de bloquer en attendant une saisie clavier. Il n'y a personne à 22h — sans ce flag, la tâche se gèle dès le premier Read-Host.
New-ScheduledTaskTrigger -Daily -At "22:00" — tous les jours à 22h00. Autres déclencheurs disponibles : -Weekly, -AtStartup, -AtLogOn, ou -RepetitionInterval (New-TimeSpan -Minutes 15) pour un intervalle de 15 minutes.
-ExecutionTimeLimit (New-TimeSpan -Hours 1) — si le script tourne encore après 1 heure, Windows le tue automatiquement. Évite une tâche bloquée qui monopolise le système indéfiniment.
-StartWhenAvailable — si le PC était éteint à 22h, la tâche se lance dès le prochain démarrage. Sans ce flag, la fenêtre d'exécution est simplement manquée.
-Force — si une tâche avec ce nom existe déjà, elle est mise à jour (plutôt qu'une erreur). Pratique pour déployer des mises à jour de script.
TaskPath TaskName State Enabled
-------- -------- ----- -------
\ GSB-Sauvegarde-Nuit Ready True
Gérer ses tâches planifiées en ligne de commande
| Action | Commande PowerShell |
|---|---|
| Voir toutes les tâches | Get-ScheduledTask |
| Détail + dernier résultat | Get-ScheduledTaskInfo "GSB-Sauvegarde-Nuit" — LastTaskResult : 0 = succès |
| Lancer manuellement | Start-ScheduledTask -TaskName "GSB-Sauvegarde-Nuit" |
| Désactiver (pause) | Disable-ScheduledTask -TaskName "GSB-Sauvegarde-Nuit" |
| Supprimer | Unregister-ScheduledTask -TaskName "GSB-Sauvegarde-Nuit" -Confirm:$false |
Compte de service et principe du moindre privilège
En entreprise, on ne fait jamais tourner une tâche planifiée sous un compte administrateur personnel. On crée un compte de service dédié (ex. svc_sauvegarde) avec uniquement les droits nécessaires : lire le dossier source, écrire la destination. Si ce compte est compromis, l'attaquant ne peut rien faire d'autre que lire et écrire dans ces deux dossiers. C'est le principe du moindre privilège — pilier de la sécurité informatique.
Linux — cron et crontab
Sous Linux, c'est le démon cron qui gère les tâches planifiées. Chaque utilisateur a son crontab, un fichier de tâches édité avec crontab -e.
La syntaxe cron — 5 champs
# min heure jour_du_mois mois jour_semaine commande
0 22 * * * /bin/bash /home/marc/sauvegarde.sh
expl.
Chaque champ peut être : un nombre, * (n'importe quel), une liste 1,3,5, une plage 1-5, ou un pas */15 (toutes les 15 unités).
| Expression cron | Signification |
|---|---|
0 22 * * * | Chaque jour à 22h00 |
0 8 * * 1-5 | Du lundi au vendredi à 8h00 |
*/15 * * * * | Toutes les 15 minutes |
0 0 1 * * | Le 1er de chaque mois à minuit |
30 9 * * 1 | Chaque lundi à 9h30 |
@reboot | Au démarrage du système (alias spécial) |
@daily | Équivalent de 0 0 * * * — minuit chaque jour |
Ajouter sa première tâche cron
# Dans ta VM Linux
crontab -e
expl.
Ouvre ton crontab dans l'éditeur par défaut (souvent nano). Ajoute une ligne de tâche en bas, enregistre, ferme. cron relit automatiquement le fichier — aucun redémarrage nécessaire.
# Sauvegarde chaque jour à 22h — capture la sortie ET les erreurs dans le log
0 22 * * * /bin/bash /home/marc/sauvegarde.sh >> /home/marc/sauvegarde.log 2>&1
expl.
>> /home/marc/sauvegarde.log — redirige la sortie standard en ajout vers le fichier de log. Sans ça, l'affichage du script disparaît dans le vide (il n'y a personne devant le terminal à 22h).
2>&1 — redirige le flux d'erreurs (numéro 2) vers le même endroit que la sortie standard (numéro 1). Sans ça, les erreurs sont perdues : tu ne saurais jamais pourquoi le script a planté.
Commandes cron essentielles
| Commande | Ce qu'elle fait |
|---|---|
crontab -e | Éditer ses tâches cron |
crontab -l | Lister ses tâches actuelles (sans les modifier) |
crontab -r | Supprimer toutes ses tâches — irréversible, à utiliser avec précaution |
systemctl status cron | Vérifier que le démon cron tourne (Ubuntu/Debian) |
grep CRON /var/log/syslog | Voir les exécutions passées et leurs éventuelles erreurs |
Erreur courante (cron)
le script fonctionne en manuel mais pas en tâche planifiée
Cause classique : cron s'exécute avec un environnement minimal. La variable $PATH est réduite à /usr/bin:/bin — une commande comme python3 ou node trouvée dans ton terminal peut être introuvable pour cron.
Solution 1 : utilise des chemins absolus dans ton script (/usr/bin/python3 plutôt que python3). Trouve le chemin exact avec which python3.
Solution 2 : ajoute en tête de ton crontab : PATH=/usr/local/bin:/usr/bin:/bin
Tester sa syntaxe cron sans attendre
Le site crontab.guru traduit instantanément une expression cron en langage courant (0 22 * * * → « À 22:00 chaque jour »). Utilise-le pour vérifier avant de déployer.
Windows vs Linux
comment décider ?
Tu gères un parc Windows → Task Scheduler PS ou interface taskschd.msc.
Le script tourne sur un serveur Linux (VM, NAS, Raspberry Pi, hébergement) → cron, universel et léger.
Les deux cohabitent souvent : un cron Linux peut appeler un script PowerShell via SSH, et un script PS peut synchroniser vers un serveur Linux.
Pour finir
Ton antisèche (les briques)
| Brique | PowerShell | bash |
|---|---|---|
| Afficher | Write-Host "..." | echo "..." |
| Variable | $x = "val" | x="val" (sans espaces) |
| Demander | Read-Host "?" | read -p "?" x |
| Condition | if (...) { } else { } | if [ ... ]; then ... fi |
| Plusieurs cas | switch ($x) { 1 {} default {} } | case $x in ... esac |
| Liste | @("a","b") · $l[0] · $l += "c" | l=(a b) · ${l[0]} |
| Boucle liste | foreach ($f in $l) { } | for f in *; do ... done |
| Boucle compteur | for ($i=1; $i -le 5; $i++) { } | for i in {1..5}; do ... done |
| Fonction | function F { param($a) ... } | f() { ... $1 ... } |
| Argument du script | param($a) · $args | $1 $2 · $@ |
| Commentaire | # | # |
Chiffres-repères et limites pratiques
| Grandeur | Valeur repère | Pourquoi ça compte |
|---|---|---|
| Taille raisonnable d'un script | < 200-300 lignes par fichier | Au-delà, découpe en fonctions ou en modules séparés (.psm1) — sinon impossible à maintenir |
[int] PowerShell (int32) | −2 147 483 648 → +2 147 483 647 | Dépasser = dépassement silencieux (overflow). Pour des tailles de fichiers en octets sur un grand disque, utilise [int64] ou [long] |
| Vitesse de copie locale (SSD → SSD) | ~500 Mo/s | 50 Go ≈ 1 min 40 s — planifie la nuit, pas pendant les heures de bureau |
| Vitesse de copie réseau (LAN 1 Gb) | ~100-115 Mo/s réels | 50 Go → 7-8 min ; au-delà de 30 min, réseau ou disque cible saturé |
Get-Content sur un gros fichier | Charge tout en RAM | Un log de 500 Mo consomme 500 Mo de RAM d'un coup. Utilise Get-Content -ReadCount 1000 (par lots) pour les gros fichiers |
| Conservation des logs RGPD | Souvent 90 jours en entreprise | Au-delà, les données de connexion nominatives doivent être supprimées (RGPD art. 5 — principe de minimisation) |
| Durée d'un script interactif avant que l'utilisateur croit que ça a planté | < 30 secondes sans retour visible | Au-delà, ajoute un Write-Host "En cours..." de progression — sinon l'utilisateur ferme le terminal |
Nombre de fichiers gérables par boucle foreach | Jusqu'à ~100 000 sans problème notable | Pour des millions de fichiers, passe au pipeline Get-ChildItem | ForEach-Object qui traite en flux et ne charge pas tout en mémoire |
PowerShell, bash, ou Python — quand choisir quoi ?
| Critère | PowerShell | bash | Python |
|---|---|---|---|
| OS naturel | Windows (+ Linux/Mac avec PS 7) | Linux / macOS | Tous (interpréteur à installer) |
| Administration système Windows | Excellent (AD, registre, services, IIS, Task Scheduler) | Difficile | Via bibliothèques (pywin32) |
| Administration système Linux | Possible mais non natif | Excellent (processus, fichiers, réseau, cron) | Bon via bibliothèques |
| Traitement CSV / données | Bon (Import-Csv) | Laborieux (awk, cut) | Excellent (csv intégré, pandas, openpyxl) |
| API web / JSON | Bon (Invoke-RestMethod) | Possible (curl + jq) | Excellent (requests, json intégré) |
| Déploiement sans installation | Oui — PS 5.1 présent sur tout Windows | Oui — bash présent sur tout Linux/macOS | Non — Python à installer sur chaque machine |
| En BTS SIO | Scripts d'admin Windows, TP terminal | Scripts Linux dans la VM | Développement web (hors scope SLAM), analyse de données |
Règle simple pour choisir
Tu pilotes des services ou fichiers Windows → PowerShell. Tu enchaînes des commandes sur un serveur Linux → bash. Tu traites des données complexes (JSON, Excel, API) ou tu veux un programme réutilisable multiplateforme → Python. Les trois coexistent : un script bash peut appeler Python, un script PS peut appeler un script bash via WSL.
Vérifie tes acquis
Exercice Réussi À revoir
- Qu'est-ce qu'un script, et dans quel ordre ses lignes s'exécutent-elles ?
- À quoi sert une variable ? Pourquoi faut-il parfois
[int]sur une réponse deRead-Host? - Quand préfères-tu un
switchà une suite deif/elseif? - Comment crée-t-on une liste, et à partir de quel index commence-t-elle ?
- Quelle est la différence entre une boucle
foret une boucleforeach? Et quand utiliserdo…while? - À quoi sert
param()en tête de script ? Comment lance-t-on alors le script ? - Sous Linux, à quoi sert
chmod +xavant de lancer un script ? - En bash, quelle erreur produit
nom = "GSB"(avec espaces) et pourquoi ? - Quelle est la différence entre
"..."et'...'en PowerShell ? Donne un exemple où les simples sont obligatoires. - Que fait
$ErrorActionPreference = "Stop"et pourquoi l'utiliser dans un script de sauvegarde automatique ?
Voir le corrigéMasquer le corrigé de l'exercice
Q1 — Un script est un fichier texte de commandes exécutées de haut en bas, dans l'ordre exact où elles sont écrites. Inverser deux lignes = inverser deux actions.
Q2 — Une variable mémorise une valeur pour la réutiliser. Read-Host renvoie toujours du texte : si tu veux comparer ou calculer un nombre, [int] le convertit (sinon "18" + 2 → "182" par collage).
Q3 — switch est préférable quand on compare une seule variable à plusieurs valeurs fixes (menu, extension, code). Pour des tests variés combinant plusieurs variables, reste sur if/elseif.
Q4 — $liste = @("a","b","c") ; l'index commence à 0. Dernier élément = [.Count - 1], ou [-1] directement.
Q5 — for compte (tu connais le nombre de tours à l'avance) ; foreach parcourt une collection (fichiers, liste). do…while s'utilise quand le bloc doit tourner au moins une fois (validation de saisie).
Q6 — param($a) déclare les arguments attendus. On lance : .\script.ps1 valeurA (positionnel) ou .\script.ps1 -a valeurA (nommé).
Q7 — chmod +x donne le bit « exécutable » au fichier. Sans lui : Permission denied. Linux distingue les droits lire/écrire/exécuter par fichier — une sécurité absente sous Windows.
Q8 — bash interprète nom comme une commande à lancer (avec = et "GSB" comme arguments). Message : nom : commande introuvable. Solution : coller les espaces (nom="GSB").
Q9 — "..." interprète les $variables ; '...' garde tout littéral. Obligatoire pour les mots de passe ou les regex contenant des $ qu'on ne veut pas substituer.
Q10 — Transforme toute erreur en exception attrapable par catch. Sans ça, un script de sauvegarde peut afficher une erreur rouge et continuer en « confirmant » une sauvegarde qui n'a pas eu lieu — le pire bug possible pour un outil de production.
Tu avais trouvé ?
Nuances métier — ce qu'on ne te dit pas toujours
| Sujet | Réalité pro |
|---|---|
| Scripts en production | Un script qui tourne en tâche planifiée (Task Scheduler) n'a pas de terminal : tous les Write-Host disparaissent. Utilise Add-Content pour journaliser, et vérifie le code de retour (exit 0/1) — c'est le seul retour que tu as. |
| Droits d'exécution | Un script de nettoyage lancé en tant qu'utilisateur standard ne peut pas toucher C:\Windows\. En entreprise, les scripts d'administration tournent sous un compte de service dédié (principe du moindre privilège), jamais en admin permanent. |
| RGPD et logs | Un log qui contient "Marc Dupont s'est connecté à 08:42" est une donnée personnelle au sens du RGPD. Conservation limitée (souvent 90 jours en entreprise), accès restreint, pas de log en texte clair sur un partage ouvert. |
| Encodage | Un fichier CSV lu par un script PS et écrit dans un autre outil (Excel, Linux, web) doit être en UTF-8 sans BOM. L'encodage par défaut de PowerShell 5.1 (Windows) est UTF-16 ; PowerShell 7+ utilise UTF-8. Toujours préciser -Encoding UTF8. |
| Dette technique | Un script de 300 lignes sans fonctions ni commentaires est impossible à maintenir. La règle de l'industrie : une fonction = une chose, max ~20-30 lignes, commentaire sur le pourquoi (pas le comment). Un collègue doit comprendre ton script dans 6 mois. |
| Sécurité des mots de passe | Ne jamais écrire un mot de passe en clair dans un script ($pass = "secret"). Si le script est dans un repo Git (même privé), le mot de passe peut fuiter. Utilise Read-Host -AsSecureString ou les variables d'environnement système configurées à part. |
Tu es bloqué ?
« exécution de scripts désactivée » ? → Set-ExecutionPolicy -Scope CurrentUser RemoteSigned (chapitre 1).
« accolade manquante » ? → compte tes { } et ( ), ils vont par paires.
Un calcul donne un collage (ex. 182 au lieu de 20) ? → convertis en nombre avec [int] (chapitre 4).
Un index de liste renvoie du vide ? → souviens-toi qu'on compte à partir de 0 ; dernier index = .Count - 1.
Le script bash refuse de se lancer (« Permission denied ») ? → chmod +x oublié.
En bash, « commande introuvable » sur une affectation ? → tu as mis des espaces autour du =.
Une variable affiche du vide ? → vérifie l'orthographe exacte du nom (et le $).
Un mot incompris ? → glossaire ci-dessous.
Glossaire
| Mot | Définition |
|---|---|
| Script | fichier texte contenant une suite de commandes exécutées dans l'ordre |
| Variable | une boîte nommée qui contient une valeur réutilisable |
| Type (texte / nombre) | nature d'une valeur : on calcule avec un nombre, on assemble du texte |
if / else | condition : faire une chose ou une autre selon un test vrai/faux |
switch | tester une variable contre plusieurs valeurs (menu, code, extension) |
| Liste (tableau) | variable contenant plusieurs valeurs, accessibles par index (à partir de 0) |
Boucle (foreach, for, while) | répéter une action (sur chaque élément, en comptant, ou tant qu'une condition tient) |
| Fonction | bloc de code nommé et réutilisable, qui peut prendre des paramètres et renvoyer un résultat |
| Paramètre / argument | info donnée à une fonction ou à un script au moment de l'appel |
Commentaire (#) | ligne ignorée par la machine, note pour l'humain |
Shebang (#!/bin/bash) | 1re ligne d'un script Linux qui indique l'interpréteur |
chmod +x | donne à un fichier le droit d'être exécuté (Linux) |
La prochaine fois
Tu sais piloter et automatiser une machine. On passe au réseau : comment les machines se parlent entre elles (adresses IP, ping), pour relier tes VM et préparer les services.
Compétences mobilisées
référentiel BTS SIO SLAM
Bloc 1 · Mettre à disposition des utilisateurs un service informatique
→ Installer et configurer des services
Bloc 2 · Concevoir et développer une solution applicative
→ Développer des applications en respectant les standards de programmation