Aller au contenu
Réussir mon BTS SIO

Tape une notion, une commande ou le nom d'une épreuve.

Par exemple

Chapitre 816 minDébutant

Fichiers, CSV et JSON : Import-Csv, ConvertFrom-Json et jq

Par Josselyn C., prof de BTS SIO. Mis à jour le .

À la fin de ce chapitre, tu sauras

  • manipuler fichiers et dossiers en PowerShell ;
  • lire et produire un CSV sans perdre les accents ni le délimiteur ;
  • lire et écrire du JSON avec ConvertFrom-Json et ConvertTo-Json ;
  • traiter un CSV avec awk et un JSON avec jq en Bash.

4 exercices corrigés

Un script d'administration passe son temps à lire et à écrire des fichiers : des journaux en texte brut, des listes de comptes en CSV (des valeurs séparées par un délimiteur, une ligne par enregistrement) et des configurations ou des réponses d'API en JSON (des objets imbriqués écrits en texte). PowerShell transforme directement un CSV ou un JSON en objets ; Bash les traite avec cut, awk et l'outil jq. Ce chapitre montre les deux approches, et les pièges d'encodage et de délimiteur qui font perdre des heures.

Les chemins et les fichiers en PowerShell

TâcheCommande
Construire un chemin sans se tromper de barre obliqueJoin-Path $PSScriptRoot 'config.json'
Tester l'existence d'un fichier ou d'un dossierTest-Path 'D:\Exports', Test-Path $f -PathType Leaf (fichier seulement)
Le dossier parent, le nom seulSplit-Path $f -Parent, Split-Path $f -Leaf
Lister les fichiers d'une arborescenceGet-ChildItem D:\Partages -Recurse -File -Filter *.pdf
Créer un dossier (sans erreur s'il existe)New-Item -Path D:\Exports -ItemType Directory -Force
Copier, déplacer, renommerCopy-Item, Move-Item, Rename-Item
Supprimer, d'abord en simulationRemove-Item D:\Temp\* -Recurse -WhatIf

Chaque fichier renvoyé par Get-ChildItem est un objet avec Name, FullName, Length (la taille en octets), LastWriteTime, Extension. Les fichiers de plus de 30 jours d'un dossier de journaux, avec leur taille en Mo :

powershell
$limite = (Get-Date).AddDays(-30)
Get-ChildItem -Path 'C:\Scripts\Logs' -File |
    Where-Object LastWriteTime -lt $limite |
    Select-Object Name, LastWriteTime, @{ Name = 'TailleMo'; Expression = { [math]::Round($_.Length / 1MB, 2) } }

Lire et écrire du texte

powershell
$lignes = Get-Content -Path 'C:\Scripts\serveurs.txt'        # un tableau, une ligne par élément
$texte  = Get-Content -Path 'C:\Scripts\config.json' -Raw    # une seule chaîne, sauts de ligne compris
Get-Content -Path 'C:\Scripts\Logs\export.log' -Tail 20      # les 20 dernières lignes
Get-Content -Path 'C:\Scripts\Logs\export.log' -Wait         # suivre le fichier en direct, comme tail -f

Set-Content -Path rapport.txt -Value 'Première ligne' -Encoding UTF8   # remplace le contenu
Add-Content -Path rapport.txt -Value 'Ligne ajoutée' -Encoding UTF8    # ajoute à la fin

Les fichiers CSV avec Import-Csv et Export-Csv

Un CSV a une première ligne d'en-têtes, puis une ligne par enregistrement. Le délimiteur est souvent le point-virgule en France, car Excel en français utilise la virgule comme séparateur décimal. Voici postes.csv, l'inventaire simplifié de quelques postes de Sorbier :

text
Nom;Service;OS;RAMGo
PC-0001;Comptabilite;Windows 11;16
PC-0002;Commercial;Windows 10;8
PC-0003;Comptabilite;Windows 11;8
PC-0004;Logistique;Windows 10;4
PC-0005;Direction;Windows 11;32
PC-0006;Logistique;Windows 11;16

Import-Csv transforme chaque ligne en objet dont les propriétés portent le nom des colonnes :

powershell
$postes = Import-Csv -Path .\postes.csv -Delimiter ';' -Encoding UTF8
$postes | Where-Object OS -eq 'Windows 10'
text
Nom     Service    OS         RAMGo
---     -------    --         -----
PC-0002 Commercial Windows 10 8
PC-0004 Logistique Windows 10 4
powershell
$postes | Group-Object OS | Select-Object Name, Count
text
Name       Count
----       -----
Windows 11     4
Windows 10     2

Windows 10 n'est plus maintenu depuis le 14 octobre 2025 : ces deux postes sont à migrer. Cette liste est exactement ce que l'on joint à une demande de budget.

Pour écrire un CSV, on envoie des objets à Export-Csv :

powershell
$postes | Where-Object OS -eq 'Windows 10' |
    Select-Object Nom, Service |
    Export-Csv -Path .\a-migrer.csv -Delimiter ';' -NoTypeInformation -Encoding UTF8
  • -NoTypeInformation évite, dans Windows PowerShell 5.1, une première ligne #TYPE System.Management.Automation.PSCustomObject qui perturbe Excel. PowerShell 7 ne l'écrit plus par défaut ; le paramètre ne fait pas de mal.
  • -Delimiter ';' produit un fichier qu'Excel en français ouvre directement en colonnes. -UseCulture prend le séparateur des réglages régionaux de la machine.
  • Pour qu'Excel affiche correctement les accents, le fichier doit avoir un BOM : -Encoding UTF8 en 5.1, -Encoding utf8BOM en 7.
  • -Append ajoute des lignes à un CSV existant, à condition que les colonnes soient les mêmes.

ConvertFrom-Csv et ConvertTo-Csv font la même chose sur du texte en mémoire, sans fichier : pratique pour traiter la sortie d'un ancien programme qui écrit du CSV, comme schtasks /query /fo csv.

Les fichiers JSON

Le JSON (JavaScript Object Notation) représente des objets imbriqués : des paires "clé": valeur entre accolades, des listes entre crochets. C'est le format des API REST (chapitre 10) et un bon format de configuration pour un script. Voici config.json, à côté du script de surveillance des disques :

json
{
  "seuilAttention": 15,
  "seuilCritique": 10,
  "serveurs": ["srv-ad", "srv-ad2", "srv-fichiers"],
  "alerte": { "destinataire": "informatique@sorbier.example" }
}
powershell
$config = Get-Content -Path (Join-Path $PSScriptRoot 'config.json') -Raw | ConvertFrom-Json
$config.seuilCritique            # 10
$config.serveurs[1]              # srv-ad2
$config.alerte.destinataire      # informatique@sorbier.example

-Raw est indispensable : sans lui, Get-Content envoie les lignes une par une. Contrairement à un CSV, le JSON garde les types : seuilCritique est bien un nombre.

Dans l'autre sens, ConvertTo-Json transforme des objets en texte JSON :

powershell
[pscustomobject]@{ Serveur = 'srv-ad'; LibreGo = 31; Lecteurs = @('C', 'D') } | ConvertTo-Json
json
{
  "Serveur": "srv-ad",
  "LibreGo": 31,
  "Lecteurs": [
    "C",
    "D"
  ]
}

C'est la sortie de PowerShell 7, indentée de deux espaces ; Windows PowerShell 5.1 indente autrement, avec le même contenu. -Compress écrit tout sur une ligne : {"Serveur":"srv-ad","LibreGo":31,"Lecteurs":["C","D"]}.

Le texte et les CSV en Bash

Les outils Linux traitent du texte ligne par ligne. Sur le même postes.csv :

bash
cut -d';' -f1,3 postes.csv                       # colonnes 1 et 3
tail -n +2 postes.csv                            # tout sauf la première ligne (l'en-tête)
awk -F';' '$3 == "Windows 10" { print $1 " (" $2 ")" }' postes.csv
text
PC-0002 (Commercial)
PC-0004 (Logistique)

awk est le couteau suisse des fichiers en colonnes : -F';' fixe le séparateur, $1, $2… désignent les colonnes, NR le numéro de ligne, et un bloc END s'exécute à la fin. Compter les postes par système, et additionner la mémoire :

bash
awk -F';' 'NR > 1 { n[$3]++ } END { for (os in n) print os ": " n[os] }' postes.csv | sort
awk -F';' 'NR > 1 { total += $4 } END { print total " Go" }' postes.csv
text
Windows 10: 2
Windows 11: 4
84 Go

L'enchaînement sort | uniq -c | sort -rn compte les occurrences d'une valeur et les classe de la plus fréquente à la moins fréquente. uniq ne regroupe que des lignes voisines, d'où le premier sort :

bash
tail -n +2 postes.csv | cut -d';' -f2 | sort | uniq -c | sort -rn
text
      2 Logistique
      2 Comptabilite
      1 Direction
      1 Commercial

À égalité de nombre, sort -rn classe les lignes entières dans l'ordre inverse, d'où Logistique avant Comptabilite.

Le JSON en Bash avec jq

Bash ne sait pas lire le JSON. L'outil jq, à installer (sudo apt install jq : Debian 13 fournit jq 1.7.1), le filtre et le transforme. Avec le config.json ci-dessus :

bash
jq '.seuilCritique' config.json            # 10
jq -r '.alerte.destinataire' config.json   # informatique@sorbier.example
jq -r '.serveurs[]' config.json            # une ligne par serveur
jq '.serveurs | length' config.json        # 3

L'option -r (raw) affiche les chaînes sans leurs guillemets : indispensable pour utiliser le résultat dans une variable Bash. Sans elle, jq '.alerte.destinataire' affiche "informatique@sorbier.example", guillemets compris.

Sur une liste d'objets, comme postes.json :

json
[
  { "nom": "PC-0002", "service": "Commercial", "os": "Windows 10", "ramGo": 8 },
  { "nom": "PC-0005", "service": "Direction", "os": "Windows 11", "ramGo": 32 },
  { "nom": "PC-0004", "service": "Logistique", "os": "Windows 10", "ramGo": 4 }
]
bash
jq -r '.[] | select(.os == "Windows 10") | .nom' postes.json        # PC-0002 puis PC-0004
jq '[.[] | select(.ramGo >= 8)] | length' postes.json               # 2
jq -r '.[] | "\(.nom);\(.service)"' postes.json                     # une ligne CSV par poste
jq -r --arg os "Windows 11" '.[] | select(.os == $os) | .nom' postes.json   # PC-0005
Filtre jqRôle
.cle, .a.bLire une propriété, une propriété imbriquée
.[]Parcourir chaque élément d'une liste
.[0]Le premier élément
|Envoyer le résultat au filtre suivant, comme le pipe du shell
select(condition)Ne garder que ce qui vérifie la condition
[ … ]Rassembler les résultats dans une liste
lengthLongueur d'une liste ou d'une chaîne
"\(.a) \(.b)"Construire une chaîne
--arg nom valeurPasser une variable du shell, utilisée comme $nom dans le filtre

Passe toujours une valeur du shell par --arg plutôt qu'en la collant dans le filtre : une valeur contenant un guillemet casserait le filtre, comme une injection.

Exercices

Exercice 1 sur 4 Interroger un CSV Réussi À revoir

Avec $postes, l'import de postes.csv de ce chapitre, écris les commandes PowerShell qui :

  1. comptent les postes du service Comptabilite ;
  2. affichent les postes qui ont moins de 16 Go de mémoire ;
  3. calculent la mémoire totale du parc ;
  4. exportent les postes de la Logistique dans logistique.csv, avec un point-virgule et des accents lisibles dans Excel, sous PowerShell 7.

Donne le résultat des questions 1 à 3.

Voir la réponseMasquer la réponse de l'exercice 1
powershell
# 1.
@($postes | Where-Object Service -eq 'Comptabilite').Count
# 2.
$postes | Where-Object { [int]$_.RAMGo -lt 16 }
# 3.
($postes | Measure-Object -Property RAMGo -Sum).Sum
# 4.
$postes | Where-Object Service -eq 'Logistique' |
    Export-Csv -Path .\logistique.csv -Delimiter ';' -NoTypeInformation -Encoding utf8BOM
  1. 2 (PC-0001 et PC-0003).
  2. PC-0002 (8), PC-0003 (8) et PC-0004 (4). Sans la conversion [int], la comparaison de chaînes donnerait un résultat faux : "8" -lt "16" est faux, car le caractère 8 vient après 1.
  3. 84. Measure-Object convertit lui-même les chaînes en nombres pour la somme : 16 + 8 + 8 + 4 + 32 + 16.
  4. utf8BOM ajoute la marque d'ordre des octets qu'attend Excel ; sous Windows PowerShell 5.1, on écrirait -Encoding UTF8.

Tu avais trouvé ?

Exercice 2 sur 4 Le même calcul en Bash Réussi À revoir

Sur postes.csv, écris la commande Bash qui affiche le nom des postes ayant 16 Go de mémoire ou plus, puis celle qui affiche le nombre de postes par service, du plus grand nombre au plus petit. Donne la sortie de la première.

Voir la réponseMasquer la réponse de l'exercice 2
bash
awk -F';' 'NR > 1 && $4 >= 16 { print $1 }' postes.csv
tail -n +2 postes.csv | cut -d';' -f2 | sort | uniq -c | sort -rn
text
PC-0001
PC-0005
PC-0006

awk compare $4 comme un nombre parce que la valeur ressemble à un nombre : il n'a pas le piège des chaînes de PowerShell. NR > 1 saute l'en-tête, sans quoi awk comparerait le texte RAMGo à 16.

Tu avais trouvé ?

Exercice 3 sur 4 Lire une configuration Réussi À revoir

Le fichier config.json de ce chapitre est lu par un script PowerShell et par un script Bash. Écris, dans chaque langage, la ligne qui range le deuxième serveur de la liste dans une variable $second, puis la ligne qui parcourt tous les serveurs en affichant « Contrôle de srv-… ».

Voir la réponseMasquer la réponse de l'exercice 3
powershell
$config = Get-Content -Path .\config.json -Raw | ConvertFrom-Json
$second = $config.serveurs[1]
foreach ($s in $config.serveurs) { "Contrôle de $s" }
bash
second=$(jq -r '.serveurs[1]' config.json)
jq -r '.serveurs[]' config.json | while IFS= read -r s; do
    echo "Contrôle de $s"
done

Les deux langages comptent à partir de 0 : l'indice 1 désigne srv-ad2. Côté Bash, -r est indispensable, sinon $second contiendrait "srv-ad2" avec ses guillemets.

Tu avais trouvé ?

Exercice 4 sur 4 L'export qui perd ses accents Réussi À revoir

Sur srv-ad, où seul Windows PowerShell 5.1 est installé, un technicien exporte l'annuaire avec Get-ADUser -Filter * -Properties Department | Select-Object Name, Department | Export-Csv annuaire.csv. Dans le fichier, « Élise Bérard » devient « ?lise B?rard », toutes les valeurs sont entre guillemets dans une seule colonne d'Excel, et la première ligne commence par #TYPE. Explique les trois problèmes et corrige la commande.

Voir la réponseMasquer la réponse de l'exercice 4
  1. Les accents : sans -Encoding, Export-Csv de Windows PowerShell 5.1 écrit en ASCII, qui ne connaît pas les lettres accentuées et les remplace par ?. La perte est définitive.
  2. Une seule colonne : le délimiteur par défaut est la virgule, alors qu'Excel en français attend un point-virgule.
  3. La ligne #TYPE : Windows PowerShell 5.1 écrit le type des objets en tête de fichier, sauf avec -NoTypeInformation.
powershell
Get-ADUser -Filter * -Properties Department | Select-Object Name, Department |
    Export-Csv -Path annuaire.csv -Delimiter ';' -NoTypeInformation -Encoding UTF8

En 5.1, -Encoding UTF8 écrit un BOM, ce qui permet à Excel de reconnaître l'UTF-8 et d'afficher « Élise Bérard ».

Tu avais trouvé ?

Chapitre terminé !

Fais les 4 exercices avant de passer à la suite : c'est en t'entraînant que tout se fixe.

chapitres
–
de série
–
du cours
–
Chapitre suivant : Expressions régulières : -match, Select-String, grep et sedRevoir le sommaire du cours