Lire et écrire des fichiers avec PowerShell
Rappel: Travailler avec les chemins d'accès
Avant de lire ou d'écrire des fichiers, il est important de comprendre comment spécifier les chemins d'accès. Il existe deux types de chemins : les chemins absolus et les chemins relatifs.
- Chemin absolu : Un chemin complet qui commence à la racine du système de fichiers, par exemple
C:\Users\NomUtilisateur\Documents\fichier.txt. Un chemin absolu commencera toujours par une lettre de lecteur suivie de:\. - Chemin relatif : Un chemin qui est relatif au répertoire de travail actuel, par exemple
.\fichier.txtpour un fichier dans le répertoire courant. Un chemin relatif commencera toujours par.\ou..\.
Commencez par créer un dossier de travail pour vos fichiers PowerShell. Par exemple, créez un dossier C:\Temps\PS-files et naviguez-y dans PowerShell :
New-Item -ItemType Directory -Path C:\Temps\PS-files
Set-Location -Path C:\Temps\PS-files
Rappel: Vous pourriez aussi utiliser cd pour changer de répertoire au lieu de Set-Location.
Ici, nous avons spécifié un chemin absolu pour créer le dossier, puisqu'il commençait par une lettre de lecteur (C:). Nous aurions aussi pu utiliser un chemin relatif. Par exemple, si vous étiez déjà dans C:\Temps, vous pourriez simplement faire :
New-Item -ItemType Directory -Path .\PS-files
Set-Location -Path .\PS-files
Les commandes Test-Path, Split-Path, Join-Path et Resolve-Path
PowerShell offre plusieurs commandes utiles pour manipuler les chemins d'accès :
Test-Path: Vérifie si un chemin existe (retourne$trueou$false).Split-Path: Divise un chemin en ses composants (répertoire, nom de fichier).Join-Path: Combine plusieurs segments de chemin en un seul.Resolve-Path: Résout un chemin relatif en un chemin absolu.
Commençons par créer un fichier texte simple dans notre répertoire de travail en utilisant un chemin relatif :
$cheminRelatif = ".\monfichier.txt"
New-Item -ItemType File -Path $cheminRelatif
Vérifions que le fichier a été créé avec Test-Path :
Test-Path -Path $cheminRelatif # Devrait retourner True
Changeons de répertoire pour voir le fichier :
cd ..
Test-Path -Path $cheminRelatif
Ici, PowerShell devrait retourner False, car nous avons changé de répertoire et le chemin relatif n'est plus valide. Pour accéder au fichier, nous devrions utiliser un chemin absolu ou revenir dans le répertoire où le fichier a été créé.
Revenons dans le répertoire où le fichier a été créé, puis utilisons Resolve-Path pour obtenir le chemin absolu du fichier :
cd .\PS-files
$cheminAbsolu = Resolve-Path -Path $cheminRelatif
$cheminAbsolu # Affiche le chemin absolu du fichier
Le chemin absolu sera quelque chose comme C:\Temps\PS-files\monfichier.txt, ce qui est bel et bien un chemin absolu.
Maintenant, nous pourrions utiliser ce chemin absolu pour accéder au fichier, peu importe où nous sommes dans le système de fichiers.
cd ..
Test-Path -Path $cheminAbsolu # Devrait retourner True
Explorons maintenant la commande Split-Path pour diviser le chemin en ses composants :
Split-Path -Path $cheminAbsolu -Parent # Affiche le répertoire parent
Split-Path -Path $cheminAbsolu -Leaf # Affiche le nom du fichier
Dans les versions PowerShell supérieures à 5.1, Split-Path peut également être utilisé avec le paramètre -Extension pour obtenir l'extension du fichier :
Split-Path -Path $cheminAbsolu -Extension # Affiche l'extension du fichier, soit ".txt"
Split-Path est utilisé avec un des paramètres -Parent, ou -Leaf pour obtenir respectivement le répertoire parent ou le nom du fichier.
Nous aurions aussi pu utiliser Split-Path avec le chemin relatif. Dans ce cas, le répertoire parent serait relatif au répertoire courant.
Split-Path -Path $cheminRelatif -Parent # Affiche le répertoire parent relatif, soit "."
Split-Path -Path $cheminRelatif -Leaf # Affiche le nom du fichier
Finalement, utilisons Join-Path pour combiner des segments de chemin. Par exemple, si nous avons un répertoire et un nom de fichier séparés, nous pouvons les joindre :
$repertoire = "C:\Temps\PS-files"
$nomFichier = "mon-deuxieme-fichier.txt"
$cheminComplet = Join-Path -Path $repertoire -ChildPath $nomFichier
$cheminComplet # Affiche C:\Temps\PS-files\mon-deuxieme-fichier.txt
Cela crée un chemin complet en combinant le répertoire et le nom du fichier. Cette commande est particulièrement utile pour construire des chemins de manière dynamique, par exemple dans des scripts où les noms de fichiers ou les répertoires peuvent varier.
Par exemple, nous avions exploré ce script plus tôt dans le cours :
```powershell
param (
[string]$source,
[string]$destination
)
$date = Get-Date -Format "yyyyMMdd_HHmmss"
$backupPath = Join-Path -Path $destination -ChildPath "Backup_$date"
Compress-Archive -Path $source -DestinationPath "$backupPath.zip"
Write-Host "Sauvegarde de $source vers $backupPath.zip effectuée avec succès."
Ici, Join-Path est utilisé pour créer un chemin de sauvegarde dynamique en combinant le répertoire de destination avec un nom de fichier basé sur la date et l'heure actuelles.
Lire et écrire des fichiers texte (brut)
Par défaut, PowerShell affiche le résultat des commandes dans la console. En fait, sans le savoir, toutes les commandes PowerShell produisent des objets qui sont ensuite convertis en texte pour l'affichage. Cependant, il est souvent nécessaire de lire ou d'écrire des fichiers texte pour stocker ou récupérer des données.
Pour afficher le résultat d'une commande dans un fichier texte, nous pouvons utiliser la redirection de sortie avec Out-File ou l'opérateur > (ou >> pour ajouter à un fichier existant).
Get-Process | Out-File -FilePath "processus.txt"
ou
Get-Process > "processus.txt"
Ces commandes enverront la liste des processus en cours d'exécution dans le fichier processus.txt.
Pour ajouter à un fichier existant, utilisez l'option -Append avec Out-File ou l'opérateur >> :
Get-Process | Out-File -FilePath "processus.txt" -Append
ou
Get-Process >> "processus.txt"
Rappel:
Out-Fileou>écrase le fichier s'il existe déjà. Si le fichier n'existe pas, il sera créé.Out-File -Appendou>>ajoute au fichier s'il existe déjà. Si le fichier n'existe pas, il sera créé.
La commande Out-File est pratique mais comporte quelques limitations. Par exemple, elle ne gère pas bien les encodages de texte autres que UTF-16 par défaut, et elle peut être moins efficace pour de très grands fichiers.
Dans ces cas, il est souvent préférable d'utiliser Set-Content/Add-Content pour écrire dans un fichier et Get-Content pour lire un fichier.
Set-Content -Path "processus.txt" -Value (Get-Process)
ou pour ajouter à un fichier existant :
Add-Content -Path "processus.txt" -Value (Get-Process)
Examinez les fichiers générés pour voir la différence de formatage entre Out-File et Set-Content/Add-Content. Avec Out-File, le formatage est plus proche de ce que vous voyez dans la console, tandis que Set-Content/Add-Content écrit une représentation plus brute des objets. Pour rendre le contenu plus lisible, vous pouvez convertir les objets en chaînes de caractères avant de les écrire dans le fichier :
Set-Content -Path "processus.txt" -Value (Get-Process | Out-String)
Ou sélectionner des propriétés spécifiques à écrire :
Set-Content -Path "processus.txt" -Value (Get-Process | Select-Object Name, Id, CPU)
Dans ces cas, nous avons utilisé le paramètre -Value pour spécifier le contenu à écrire dans le fichier. Nous aurions également pu utiliser le pipeline, ce qui est souvent plus lisible :
Get-Process | Select-Object Name, Id, CPU | Set-Content -Path "processus.txt"
ou
Get-Process | Select-Object Name, Id, CPU | Add-Content -Path "processus.txt"
Pour lire le contenu d'un fichier texte, utilisez Get-Content :
Get-Content -Path "processus.txt"
Cela affichera le contenu du fichier processus.txt dans la console. Vous pouvez également stocker le contenu dans une variable pour un traitement ultérieur :
$contenu = Get-Content -Path "processus.txt"
$contenu # Affiche le contenu stocké dans la variable
Par défaut, Get-Content lit le fichier ligne par ligne et retourne un tableau de chaînes, chaque élément représentant une ligne du fichier.
Nous pouvons accéder à des lignes spécifiques en utilisant l'indexation de tableau. Par exemple, pour obtenir la première ligne du fichier :
$premiereLigne = $contenu[0]
$premiereLigne # Affiche la première ligne du fichier
Une façon courante de lire un fichier et de traiter chaque ligne est d'utiliser une boucle foreach :
foreach ($ligne in Get-Content -Path "processus.txt") {
Write-Host "Ligne: $ligne"
}
Si vous souhaitez lire le fichier en une seule chaîne, vous pouvez utiliser le paramètre -Raw :
Get-Content -Path "processus.txt" -Raw
Pour les commandes Set-Content, Add-Content et Get-Content, le paramètre -Path est un paramètre positionnel, donc vous pouvez omettre -Path et simplement fournir le chemin en premier argument :
Get-Content "processus.txt"
// Equivalent à Get-Content -Path "processus.txt"
Perte des données en format texte
Écrire des objets complexes dans un fichier texte peut être pratique dans certains contextes, mais cela entraîne une perte d'information, car les fichiers texte ne peuvent contenir que des chaînes de caractères. Par exemple, si vous écrivez un objet avec plusieurs propriétés dans un fichier texte, vous ne pourrez pas facilement récupérer ces propriétés individuellement lors de la lecture du fichier.
Considérez l'exemple suivant où nous écrivons des objets Get-Process dans un fichier texte :
Get-Process | Set-Content -Path "processus.txt"
Le fichier texte contiendra une représentation textuelle des processus, mais vous ne pourrez pas facilement les retransformer en objets PowerShell. Si vous essayez de relire ce fichier avec Get-Content, vous obtiendrez simplement des lignes de texte, pas des objets processus.
$contenu = Get-Content -Path "processus.txt"
$contenu | Get-Member
Cela montrera que $contenu est un tableau de chaînes (String), et non des objets Process. Nous ne pourrons pas accéder aux propriétés des processus comme Name, Id, etc, ni aux méthodes Start(), Stop(), etc. Il sera également impossible de les envoyer à d'autres cmdlets qui attendent des objets Process, comme Stop-Process.
Pour éviter cette perte d'information, il est préférable d'utiliser des formats de fichiers qui conservent la structure des données, comme CSV ou JSON, que nous verrons plus loin.
Lire et écrire des fichiers CSV
PowerShell offre des cmdlets spécifiques pour travailler avec les fichiers CSV (Comma-Separated Values), qui sont couramment utilisés pour stocker des données tabulaires.
Import-Csv: Lit un fichier CSV et le convertit en objets PowerShell.Export-Csv: Convertit des objets PowerShell en format CSV et les écrit dans un fichier.
Nous utiliserons encore une fois la commande Get-Process pour démontrer comment exporter des données vers un fichier CSV et les réimporter.
Get-Process | Select-Object Name, Id, CPU -First 10 | Export-Csv -Path '.\procs.csv'
Ici, Select-Object est utilisé pour sélectionner uniquement les propriétés Name, Id et CPU des objets processus avant de les exporter vers un fichier CSV. Nous aurions pu omettre Select-Object pour exporter toutes les propriétés, mais cela aurait créé un fichier CSV très volumineux et difficile à lire.
Par défaut, Export-Csv inclut une ligne d'en-tête avec le nom du type d'objet.
Le fichier CSV généré ressemblera à ceci (que vous pouvez visualiser avec la commande Get-Content que nous venons de voir!)c :
PS C:\Users\Administrateur> Get-content .\procs.csv
#TYPE Selected.System.Diagnostics.Process
"Name","Id","CPU"
"AggregatorHost","3444","0,046875"
"conhost","2676","0,015625"
"csrss","428","0,953125"
"csrss","536","0,640625"
"ctfmon","4680","0,46875"
"dfsrs","2852","1,578125"
"dfssvc","2460","0,015625"
"dllhost","2824","0,0625"
"dllhost","3692","0,09375"
"dllhost","3700","0,3125"
Pour éviter que Export-Csv ajoute une ligne d'en-tête correspondant aux types d'objets, vous pouvez utiliser le paramètre -NoTypeInformation :
Get-Process | Select-Object Name, Id, CPU -First 10 | Export-Csv -Path "procs.csv" -NoTypeInformation
Cela produira un fichier CSV sans la ligne d'information sur le type, ce qui est souvent préférable pour les fichiers destinés à être lus par d'autres applications.
"Name","Id","CPU"
"AggregatorHost","3444","0,046875"
"conhost","2676","0,015625"
"csrss","428","0,953125"
"csrss","536","0,640625"
"ctfmon","4680","0,46875"
"dfsrs","2852","1,578125"
"dfssvc","2460","0,015625"
"dllhost","2824","0,0625"
"dllhost","3692","0,09375"
Cela produira un fichier CSV sans la ligne d'information sur le type, ce qui est souvent préférable pour les fichiers destinés à être lus par d'autres applications.
Maintenant que nous avons un fichier CSV, voyons comment l'importer et accéder aux données qu'il contient.
$procs = Import-Csv -Path '.\procs.csv'
$procs | Get-Member
$procs[0].Name
Ici, Import-Csv lit le fichier CSV et crée un tableau d'objets PowerShell, où chaque objet représente une ligne du fichier CSV avec des propriétés correspondant aux en-têtes de colonne. Nous pouvons utiliser Get-Member pour voir les propriétés disponibles sur ces objets. Nous pouvons accéder aux propriétés de chaque objet comme nous le ferions avec n'importe quel autre objet PowerShell. Nous avons ainsi évité la perte d'information que nous aurions eue en utilisant un fichier texte brut.
Les fichiers JSON
PowerShell prend également en charge le format JSON (JavaScript Object Notation) pour la sérialisation et la désérialisation des données.
ConvertTo-Json: Convertit des objets PowerShell en format JSON.ConvertFrom-Json: Convertit une chaîne JSON en objets PowerShell.
Exemple d'utilisation de ConvertTo-Json et ConvertFrom-Json
Pour convertir des objets PowerShell en JSON et les écrire dans un fichier, vous pouvez utiliser ConvertTo-Json :
$donnees = Get-Process | Select-Object Name, Id, CPU
$json = $donnees | ConvertTo-Json
Set-Content -Path ".\procs.json" -Value $json
Ici, Get-Process récupère les processus en cours, Select-Object sélectionne les propriétés souhaitées, ConvertTo-Json convertit les objets en une chaîne JSON, et Set-Content écrit cette chaîne dans un fichier JSON.
Le fichier JSON généré ressemblera à ceci :
[
{
"Name": "AggregatorHost",
"Id": 3444,
"CPU": 0.046875
},
{
"Name": "conhost",
"Id": 2676,
"CPU": 0.015625
},
{
"Name": "csrss",
"Id": 428,
"CPU": 0.953125
},
...
Nous voyons que nous avons un tableau JSON où chaque élément est un objet avec les propriétés Name, Id et CPU.
Pour lire un fichier JSON et le convertir en objets PowerShell, vous pouvez utiliser ConvertFrom-Json :
$json = Get-Content -Path "procs.json" -Raw
$donnees = $json | ConvertFrom-Json
foreach ($processus in $donnees) {
Write-Host "Nom: $($processus.Name), Id: $($processus.Id), CPU: $($processus.CPU)"
}
Ici, Get-Content lit le contenu du fichier JSON, ConvertFrom-Json convertit la chaîne JSON en objets PowerShell, et une boucle foreach est utilisée pour afficher les propriétés de chaque objet. Comme pour les fichiers CSV, ConvertFrom-Json crée des objets avec des propriétés correspondant aux clés dans le JSON.
Le paramètre -Raw de Get-Content est important ici pour s'assurer que le contenu du fichier JSON est lu en une seule chaîne, ce qui est nécessaire pour que ConvertFrom-Json fonctionne correctement.
Les fichiers HTML
PowerShell permet également de générer et de manipuler des fichiers HTML, ce qui est utile pour créer des rapports ou des pages web simples. Pour générer une page HTML, nous utiliserons ConvertTo-Html qui convertit des objets PowerShell en format HTML.
Exemple d'utilisation de ConvertTo-Html
$donnees = Get-Process | Select-Object Name, Id, CPU
$donnees | ConvertTo-Html | Set-Content -Path "procs.html"
Ici, Get-Process récupère les processus en cours, Select-Object sélectionne les propriétés souhaitées, ConvertTo-Html convertit les objets en une chaîne HTML, et Set-Content écrit cette chaîne dans un fichier HTML.
Le fichier HTML généré contiendra une table avec les en-têtes de colonne correspondants aux propriétés sélectionnées.
Le fichier HTML généré ressemblera à ceci :
PS C:\Users\Administrateur> get-content procs.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>HTML TABLE</title>
</head><body>
<table>
<colgroup><col/><col/><col/></colgroup>
<tr><th>Name</th><th>Id</th><th>CPU</th></tr>
<tr><td>AggregatorHost</td><td>3444</td><td>0,046875</td></tr>
<tr><td>conhost</td><td>2676</td><td>0,015625</td></tr>
<tr><td>csrss</td><td>428</td><td>0,953125</td></tr>
<tr><td>csrss</td><td>536</td><td>1,03125</td></tr>
<tr><td>ctfmon</td><td>4680</td><td>0,46875</td></tr>
...
Un fichier HTML est cependant fait pour être visualisé dans un navigateur web. Ouvrez le fichier procs.html dans votre navigateur pour voir la table formatée.
Pour personnaliser davantage le fichier HTML, vous pouvez utiliser les paramètres de ConvertTo-Html, tels que -Title pour définir le titre de la page et -PreContent ou -PostContent pour ajouter du contenu avant ou après la table.
$donnees = Get-Process | Select-Object Name, Id, CPU
$donnees | ConvertTo-Html -Title "Rapport des Processus" -PreContent "<h1>Liste des Processus en Cours</h1>" | Set-Content -Path "procs.html"
Ici, le fichier HTML généré aura un titre "Rapport des Processus" et un en-tête <h1> avant la table.
Création d'objets personnalisés
Jusqu'à présent, nous avons vu comment écrire des données générées par des cmdlets dans des fichiers. Cependant, il est souvent nécessaire de créer des objets personnalisés pour structurer les données avant de les exporter. PowerShell permet de créer facilement des objets personnalisés à l'aide de New-Object ou de la syntaxe [pscustomobject].
La syntaxe de [pscustomobject] est la suivante:
[pscustomobject]@{
Propriete1 = Valeur1
Propriete2 = Valeur2
Propriete3 = Valeur3
}
Ceci crée un objet PowerShell avec les propriétés spécifiées. Ce nouvel objet sera de type PSCustomObject. Par exemple, pour créer un objet représentant une personne avec des propriétés comme le nom, l'âge et l'adresse, vous pouvez faire :
$personne = [pscustomobject]@{
Nom = "Dupont"
Age = 30
Adresse = "123 Rue Principale"
}
Explorons la sortie de Get-Member pour cet objet :
$personne | Get-Member
Cela affichera les membres de l'objet, y compris les propriétés que nous avons définies (Nom, Age, Adresse) ainsi que les méthodes héritées de l'objet de base, comme ToString() et Equals(). Vous verrez également que le type de l'objet est PSCustomObject.
Cette méthode est particulièrement utile pour structurer des données avant de les exporter vers des formats comme CSV ou JSON. Par exemple, si vous collectez des informations sur plusieurs utilisateurs, vous pouvez créer un tableau d'objets personnalisés et ensuite l'exporter.
$utilisateurs = @()
$utilisateurs += [pscustomobject]@{ Nom = "Dupont"; Age = 30; Adresse = "123 Rue Principale" }
$utilisateurs += [pscustomobject]@{ Nom = "Martin"; Age = 25; Adresse = "456 Avenue Secondaire" }
$utilisateurs += [pscustomobject]@{ Nom = "Durand"; Age = 40; Adresse = "789 Boulevard Tertiaire" }
$utilisateurs | Export-Csv -Path "C:\chemin\vers\le\fichier.csv" -NoTypeInformation
Ici, nous avons créé un tableau $utilisateurs contenant plusieurs objets personnalisés, chacun représentant un utilisateur avec des propriétés spécifiques. Ensuite, nous avons exporté ce tableau vers un fichier CSV.
Exemple complet : Exporter un inventaire système
Voici un exemple complet qui combine la création d'objets personnalisés et l'exportation vers un fichier CSV. Supposons que vous souhaitiez collecter des informations sur plusieurs sessions PowerShell distantes et exporter ces informations dans un fichier CSV.
$inventory = @()
foreach ($session in $sessions) {
$data = Invoke-Command -Session $session -ScriptBlock {
$hostname = hostname
$os = (Get-CimInstance Win32_OperatingSystem).Caption
$build = (Get-CimInstance Win32_OperatingSystem).BuildNumber
$totalRAM_GB = [math]::Round((Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory / 1GB, 2)
$logicalProcessors = (Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors
$freeSpace_C_GB = [math]::Round((Get-CimInstance Win32_LogicalDisk -Filter "DeviceID='C:'").FreeSpace / 1GB, 2)
$spoolerStatus = (Get-Service -Name Spooler).Status
$w32TimeStatus = (Get-Service -Name W32Time).Status
$winRMStatus = (Get-Service -Name WinRM).Status
[pscustomobject]@{
Hostname = $hostname
OS = $os
Build = $build
TotalRAM_GB = $totalRAM_GB
LogicalProcessors = $logicalProcessors
FreeSpace_C_GB = $freeSpace_C_GB
SpoolerStatus = $spoolerStatus
W32TimeStatus = $w32TimeStatus
WinRMStatus = $winRMStatus
}
}
$inventory += $data
}
$inventory | Export-Csv -Path "$env:USERPROFILE\Desktop\inventaire-baseline.csv" -NoTypeInformation