Académique Documents
Professionnel Documents
Culture Documents
1. Anatomie d’une fonction
En PowerShell et comme dans de nombreux langages, une fonction est un ensemble d’instructions auquel on donne
un nom. Le principal intérêt des fonctions est que l’on peut y faire référence à plusieurs reprises, sans avoir à
ressaisir l’ensemble des instructions à chaque fois. Une fonction est constituée des éléments suivants :
l un nom ;
l un type de portée (facultatif) ;
l un bloc param (facultatif) ;
l un bloc d’instructions.
En ce qui concerne le type de portée, nous aborderons cette notion un peu plus loin dans ce chapitre. L’écriture
d’une fonction nécessite la syntaxe suivante :
Prenons par exemple la fonction suivante :
Function Bonjour
{
$date = Get-Date
Write-Host "Bonjour, nous sommes le $date" -Foreground Yellow
}
Cette fonction est la plus basique qui soit. À chaque appel, elle affiche un message en Jaune dans la console. Pour
appeler une fonction, il suffit tout simplement de taper son nom :
PS > Bonjour
Bonjour, nous sommes le 13/01/2018 13:18:54
2. Utilisation des arguments
Une notion intéressante dans l’utilisation de fonctions ou de scripts est le passage de valeurs. Pour ce faire, une
technique consiste à utiliser les arguments. Les arguments sont les valeurs placées derrière le nom de la fonction
lorsque celleci est appelée. Voici la syntaxe de l’appel d’une fonction avec plusieurs arguments :
<Nom de fonction> <Argument1> <Argument2> <ArgumentN>
Par exemple :
Dans cet exemple, Arnaud et Robin sont les deux arguments passés à la fonction Bonjour.
Imaginons à présent que nous venions de créer une fonction qui affiche un message dans une boîte de dialogue. La
question est, comment faire pour récupérer les arguments de façon à les insérer dans une boîte de dialogue ? Eh
bien la réponse est toute simple, lorsque nous passons des arguments à une fonction (ou à un script), tous se
retrouvent stockés dans un tableau d’arguments appelé $args. Et c’est ce tableau que nous allons réutiliser à
l’intérieur de la fonction (ou du script). $args[0] correspond au premier argument, $args[1] au second, etc.
Prenons par exemple une fonction capable d’afficher une fenêtre popup comprenant un titre et un texte :
Function Show-Popup
{
$WshShell = New-Object -ComObject wscript.Shell
$WshShell.Popup($args[0], 0, ’Popup PowerShell’)
}
Cette fonction fait appel à un objet COM du nom de wscript.shell.
Lorsque nous ferons appel à cette fonction avec un ou plusieurs arguments, seul le premier sera pris en compte et
affiché dans une boîte de dialogue :
Notez que nous avons codé le titre de la fenêtre popup « en dur », mais nous aurions pu facilement le rendre
paramétrable avec $args[1]. Ceci dit, le principal problème de la technique des arguments avec $args est qu’on
ne sait jamais dans quel ordre passer les valeurs sur la ligne de commandes. Tout cela est grandement amélioré
avec la technique des paramètres, que nous allons voir tout de suite.
3. Utilisation des paramètres
La deuxième façon de transmettre des variables à une fonction ou à un script est d’utiliser les paramètres. Nous
vous recommandons vivement de privilégier les paramètres aux arguments. La syntaxe d’appel d’une fonction avec
un paramètre est la suivante :
Ensuite, pour que PowerShell les interprète, il suffit de spécifier au début de la fonction ou du script les paramètres
d’entrée grâce à l’instruction Param.
Par exemple
Function Show-Popup
{
Param([string]$message, [string]$titre)
Avec ce principe, contrairement aux arguments, l’ordre n’a aucune importance à partir du moment où l’on spécifie le
nom du paramètre. Cela signifie que les deux expressions suivantes donneront le même résultat :
Si l’on ne souhaite pas préciser les noms des paramètres quand on appelle la fonction, alors il faudra tenir compte
de l’ordre dans lequel ils ont été déclarés dans le bloc param. En effet il faut s’assurer que les valeurs soient
passées au bon paramètre.
Ainsi nous pouvons appeler la fonction Show-Popup comme indiqué cidessous :
On peut également attribuer des valeurs par défaut aux paramètres comme si on initialisait une variable.
Function Show-Popup
{
Param([string]$message=’Message...’, [string]$titre=’Titre’)
Ainsi, lors d’un appel de la fonction, si les valeurs des paramètres Titre et Message ne sont pas renseignés,
alors l’exécution se fera avec les valeurs définies par défaut dans le bloc Param.
Exemple
PS > Show-Popup
PowerShell autorise également l’appel de fonctions, scripts ou commandelettes natives, en utilisant une partie d’un
nom de paramètre. On peut ainsi raccourcir les noms des paramètres autant que l’on veut dans la mesure où il n’y a
pas d’ambiguïté entre plusieurs noms de paramètres.
Par exemple
4. Retour de valeurs
a. Retourner une valeur scalaire
Une fonction peut retourner toutes sortes d’objets. Pour ce faire, il suffit d’insérer l’objet (ou la variable
représentant l’objet) n’importe où dans la fonction (ou le script) pour que son résultat soit retourné. Prenons
l’exemple d’une fonction qui calcule la moyenne de deux nombres.
Function moyenne
{
param ([double]$nombre1, [double]$nombre2)
($nombre1 + $nombre2) /2
}
Testons à présent notre fonction :
Le résultat ainsi renvoyé par la fonction moyenne est un objet de type double, et non pas une chaîne de
caractères ; gardez bien cela à l’esprit !
Bien que l’instruction Return existe dans PowerShell, celleci est un faux ami. Cette instruction laisse à penser
que PowerShell ne retourne que ce qui est passé à cette instruction, ce qui est faux car PowerShell retourne toute
valeur (ou tout objet) non affectée à une variable. Ce n’est donc pas une bonne pratique d’utiliser Return (sauf dans
les classes où c’est obligatoire).
b. Retourner un objet
Le retour d’objets par un script ou une fonction est quelque chose de nouveau dans un Shell. En effet, cette
faculté était jusquelà généralement réservée aux langages de haut niveau tel que C# et consorts. Il va falloir s’y
faire car il s’agit de l’essence même de la philosophie PowerShell. Et c’est aussi cela qui fait sa grande force !
Mais que se passetil exactement lorsque nous exécutons les lignes de script suivantes ?
Voyons ce qu’il se passe lorsque nous exécutons les lignes de script ciaprès :
Tout simplement, Get-Process retourne une collection d’objets de type process (tous ceux dont le nom
commence par la lettre a). Cette collection est ensuite affectée dans la variable $processes. S’il existe plusieurs
processus commençant par la lettre « a », alors le type de cette variable sera un tableau, sinon un scalaire. Enfin
$processes est passée à la commande Stop-Process pour traitement et arrêt des processus contenus dans
la collection. Vous l’aurez remarqué, à aucun moment il n’est question de récupérer des chaînes de caractères, ni
de transmettre du texte.
Si vous venez de VBScript, il va falloir vous obliger à penser différemment car vous devrez vous empêcher de
retourner des « données utiles » noyées dans une chaîne de caractères telle que "La taille totale des
fichiers est de 15124 Ko.". En effet, avec un tel retour il sera très difficile d’enchaîner d’autres
traitements.
La philosophie de PowerShell est de renvoyer soit uniquement la valeur 15124, soit un objet possédant une
propriété nommée Taille ou Taille(Ko) contenant la valeur, tel que :
Taille(Ko)
----------
15124
Ne perdez pas de vue que les scripteurs PowerShell aiment utiliser le pipeline afin d’enchaîner les commandes
dans le but de faire un maximum de choses, et ce en un minimum de lignes et d’efforts.
Pour arriver à ce but ultime, vous allez devoir vous efforcer à renvoyer, soit des objets déjà existants, soit vos
propres objets.
Dans l’exemple suivant, nous montrons ce qu’il ne faut pas faire :
Function Get-FileInfo
{
Param([string]$Path)
$fichier = Get-Item $Path
"Nom : $($fichier.FullName)"
"Date de création : $($fichier.CreationTime)"
"Dernier accès : $($fichier.LastAccessTime)"
}
Bien que pas vraiment utile, cette fonction à but éducatif retourne quelques propriétés choisies, dont voici le
résultat :
Nom : C:\temp\Abonnés.txt
Date de création : 01/13/2018 16:57:20
Dernier accès : 01/13/2018 16:57:20
Même si notre fonction remplit son office, elle renvoie du texte. Donc à part avoir un but informatif pour l’utilisateur
final ou rediriger le résultat vers un fichier de log, elle ne permettra pas d’enchaîner un autre traitement.
L’idée est donc que la fonction retourne un objet. Ainsi, il sera facile d’extraire les valeurs de ses différentes
propriétés.
Voici donc le script final tel qu’il est préférable de l’écrire :
Function Get-FileInfo
{
Param([string]$Path)
$fichier = Get-Item $Path
$fichier | Select-Object FullName, CreationTime, LastAccessTime
}
À présent, testons notre nouvelle fonction :
C’est mieux, n’estce pas ? Le résultat ressemble enfin à ce que l’on est en droit d’attendre d’une fonction
PowerShell. De plus, contrairement à la version de départ de la fonction, nous pouvons à présent enchaîner le
traitement comme dans l’exemple cidessous :
Bien qu’il soit tentant (et possible) de vouloir franciser à outrance, tel que renvoyer les noms des propriétés en
français, nous vous recommandons de rester cohérent avec le nom des commandelettes qui sont en anglais. Cela
permet d’éviter le « franglais » et donc de conserver une bonne homogénéité globale.
5. Introduction aux « fonctions avancées »
Pour faire court, les fonctions avancées permettent de réaliser des fonctions qui se comportent de façon très
similaire, pour ne pas dire identique, aux commandelettes PowerShell. Cette possibilité permet aux scripteurs de
développer aisément de nouvelles commandes en PowerShell, alors qu’en principe le développement de
commandelettes nécessite l’usage d’un langage .NET de haut niveau tel que C#.
Une fonction avancée permet entre autres :
l D’accepter des objets en entrée par l’intermédiaire du pipeline,
l L’implémentation des paramètres communs : Verbose, WhatIf, ErrorAction, etc.
l La validation des valeurs passées aux paramètres,
l De rendre des paramètres obligatoires,
l La création de plusieurs jeux de paramètres distincts,
l La définition d’alias de paramètres,
l …
De plus, les fonctions avancées constituent la pierre angulaire dans la fabrication des modules PowerShell. En effet,
pour des raisons de simplicité et de rapidité de développement, de très nombreuses commandes contenues dans
les modules sont écrites en PowerShell sous forme de fonctions avancées.
a. Différences entre les fonctions classiques et les fonctions avancées
On distingue facilement les fonctions classiques des fonctions avancées car ces dernières font appel à l’attribut
CmdletBinding. Cet attribut est riche en fonctionnalités mais nous n’en dirons pas plus pour le moment car il
est presque secondaire par rapport aux autres fonctionnalités. Pour plus d’informations sur cet attribut, nous vous
invitons à consulter la rubrique d’aide :about_Functions_CmdletBindingAttribute.
Voici la syntaxe de la définition d’une fonction avancée dans sa forme la plus simple :
Comme un exemple vaut mieux qu’un long discours, voilà à quoi ressemble notre précédente fonction Show-
Popup une fois transformée en fonction avancée.
Function Show-Popup
{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[ValidateLength(1,10)]
[String]$Titre,
[Parameter(Mandatory=$false)]
[String]$Message=’Message...’
)
Process
{
$WshShell = New-Object -ComObject wscript.Shell
$WshShell.Popup($Message, 0, $Titre)
}
}
La première chose que nous pouvons remarquer est l’utilisation de l ’attribut [CmdletBinding()]. Ensuite, dans le
bloc Param, nous avons « décoré » (c’est ainsi que l’on dit dans le jargon) chaque paramètre d’un attribut
[Parameter()].
Celuici est très important car c’est grâce à ce dernier que l’on définit la nature du paramètre, c’estàdire si le
paramètre revêt un caractère obligatoire ou facultatif, s’il doit être compris entre telle et telle valeur, etc. Dans
notre exemple, nous avons rendu le paramètre -titre obligatoire. De plus, grâce à l’attribut
[ValidateLength(1,10)], nous avons fait en sorte qu’il n’accepte que des valeurs comprises entre un et dix
caractères. En faisant cela, nous nous assurons que la fonction ne s’exécutera pas si la valeur saisie n’est pas
correcte.
Nous insistons sur le fait que cette faculté de validation sur les valeurs de paramètres est vraiment très utile car
elle permet de faire en sorte que le scripteur n’ait pas à se soucier des valeurs saisies. Ainsi, ce dernier pourra se
concentrer davantage sur les actions métier du script et non pas sur des détails annexes à faible valeur ajoutée et
à haut risque d’erreur.
Enfin, vous pouvez remarquer la présence du bloc Process. Ici, dans notre exemple, nous aurions pu tout
simplement nous en passer car notre fonction ne prend pas en charge le pipeline. En effet, les blocs Begin,
Process et End ne sont utiles que dans le cas où une fonction ou un script accepte le passage de données via le
pipeline.
Pour plus d’informations sur l’attribut [Parameter()], nous vous invitons à consulter la rubrique d’aide
about_Functions_Advanced_Parameters.
b. Attributs de validation des paramètres
Les attributs de validation des paramètres, appelés également « décorateurs », sont une grande force pour les
fonctions avancées car ils permettent de valider les valeurs saisies. Afin de vous servir d’aidemémoire, nous avons
regroupé tous les décorateurs dans le tableau ciaprès :
Attribut de validation d’un paramètre Description
[Alias(<String[]>)] Définit un ou plusieurs alias sur un paramètre.
Syntaxe :
Param(
[Alias("CN","MachineName")]
[String]$ComputerName
)
[AllowNull()] Indique que le paramètre accepte une valeur nulle (ce qui
n’est pas le cas par défaut). Cet attribut est en principe
toujours associé à un paramètre obligatoire.
Syntaxe :
Param(
[Parameter(Mandatory=$true)]
[AllowNull()]
$Age
)
Param (
[Parameter(Mandatory=$true)]
[AllowEmptyString()]
$Name
)
Param (
[Parameter(Mandatory=$true)]
[AllowEmptyCollection()]
$ComputerName
)
[ValidateNotNull()] Permet de spécifier que la valeur passée en argument ne doit
pas être nulle. C’est le contraire de l’attribut [AllowNull
()].
Syntaxe :
Param (
[Parameter(Mandatory=$true)]
[ValidateNotNull()]
$ComputerName
)
[ValidateNotNullOrEmpty()] Spécifie que la valeur passée en argument ne doit pas être
nulle ou vide.
Syntaxe :
Param(
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
$ComputerName
)
[ValidateCount(<int>,<int>)] Indique un nombre minimal et maximal de valeurs que l’on
peut fournir au paramètre.
Syntaxe :
Param(
[Parameter(Mandatory=$true)]
[ValidateCount(1,5)]
[String[]]
$ComputerName
)
[ValidateLength(<int>,<int>)] Définit la longueur minimale et maximale de la valeur passée
en paramètre (nombre de caractères par exemple).
Syntaxe :
Param(
[Parameter(Mandatory=$true)]
[ValidateLength(2,10)]
[String]
$ComputerName
)
Dans cet exemple, un nom d’ordinateur devra avoir de deux
à dix caractères.
[ValidatePattern(<Regex>)] Permet de s’assurer que la valeur passée en paramètre est
conforme à un modèle établi avec les expressions régulières.
Syntaxe :
Param(
[Parameter(Mandatory=$true)]
[ValidatePattern(’ˆ\w{6}\d{4}$’)]
[String[]]
$ComputerName
)
[ValidateRange(<int>,<int>)] Permet de s’assurer que la valeur passée en paramètre a été
choisie parmi une plage de valeurs (minimale et maximale).
Syntaxe :
Param(
[Parameter(Mandatory=$true)]
[ValidateRange(0,100)]
[Int]
$Nb
)
[ValidateSet(<String[]>)] Permet de s’assurer que la valeur passée en paramètre a été
choisie parmi une liste de valeurs prédéfinie.
Syntaxe :
Param(
[Parameter(Mandatory=$true)]
[ValidateSet("Rouge", "Bleu", "Vert")]
[String]
$Couleur
)
[ValidateScript({ScriptBlock})] Un bloc de script est utilisé pour valider la valeur fournie en
paramètre. Pour que la valeur soit acceptée, le bloc de script
doit retourner la valeur $true.
Syntaxe :
Param(
[Parameter()]
[ValidateScript({$_ -ge (Get-Date)})] [DateTime]
$EventDate
)
Dans cet exemple, pour que la valeur du paramètre soit
acceptée, il faut que : 1) elle soit de type DateTime et 2)
qu’elle soit supérieure ou égale à la date courante.
Scripts
Les scripts ont un fonctionnement identique aux fonctions dans le sens où ils peuvent eux aussi bénéficier de
l’attribut [CmdletBinding] et de tout ce qui caractérise les fonctions avancées, prendre en charge des paramètres,
proposer une aide intégrée (voir plus loin), etc. La vision de Microsoft et en particulier de l’équipe PowerShell est de
permettre aux développeurs PowerShell de réaliser des scripts qui se comportent exactement comme des commandes
PowerShell natives.
D’autre part, les scripts PowerShell constituent l’enveloppe physique qui contient généralement des variables, ainsi
qu’un ensemble de fonctions. Ils portent l’extension .ps1.
Quelle que soit la version de PowerShell, même avec la version 6, tous les scripts portent l’extension .ps1.
1. Constitution d’un script
Un script est généralement constitué des parties suivantes :
Ceci étant, dans sa forme la plus minimaliste, un script ne peut contenir que le corps principal, le reste n’étant pas
obligatoire. Généralement, on va trouver sur les premières lignes d’un script quelques commentaires décrivant
succinctement :
l Le nom du script,
l Un numéro de version,
l La date de création,
l Le nom de l’auteur.
Bonne pratique : Si vous souhaitez apporter d’autres informations plus précises sur le
fonctionnement du script, par exemple sur l’usage de ses paramètres, ainsi qu’éventuellement
proposer quelques exemples d’utilisation, nous vous recommandons de fournir une aide structurée
telle que définie dans la section Aide intégrée aux scripts et fonctions. Ainsi, l’aide sera disponible à tous
vos utilisateurs en tapant la ligne de commandes suivante : help MonScript.ps1. Tout script digne de
ce nom, en entreprise, se doit de proposer une aide intégrée.
2. Commentaires
Vous le savez sans doute déjà, de par les différents exemples que nous avons fournis, les commentaires
commencent toujours par le caractère dièse « # ». Un commentaire peut être placé à n’importe quel endroit dans un
script.
Par exemple :
# MonScript.ps1
# Début du script
$nbFic = (Get-Childitem).count
Il est possible aussi de créer des blocs de commentaires grâce à la syntaxe suivante :
<#
Tout ce qui se trouve à l’intérieur d’un bloc
de commentaires est ignoré
#>
Les blocs de commentaires sont très pratiques pour commenter un grand nombre de lignes à l’intérieur d’un script.
Cela évite de devoir placer un dièse devant chaque ligne à commenter.
3. Exécution d’un script
Cela peut sembler surprenant au premier abord, mais il faut savoir que l’exécution des scripts PowerShell est
désactivée par défaut sur toutes les versions clientes de Windows. Par contre, sous Windows Server, à partir de
Windows Server 2012 R2 l’exécution est autorisée, mais seulement sous certaines conditions.
Si tel n’est pas le cas sur votre ordinateur, c’est que l’administrateur de votre entreprise a probablement configuré
une stratégie de groupe (GPO) pour autoriser l’exécution des scripts.
Pour déterminer si vous pouvez ou non exécuter des scripts, vous pouvez au choix, soit :
l Tenter l’exécution d’un script et observer le résultat,
l Utiliser la commande Get-ExecutionPolicy afin de déterminer la stratégie d’exécution courante.
L’exécution de commandes PowerShell tapées directement dans la console (mode interactif) est autorisée quelle que
soit la stratégie d’exécution.
Pour modifier la stratégie d’exécution courante, il faut :
l Démarrer la console en mode Administrateur (clic droit sur l’icône PowerShell, puis Run as Administrator),
l Taper la commande Set-ExecutionPolicy RemoteSigned.
La stratégie RemoteSigned autorise uniquement l’exécution des scripts locaux ; les scripts en provenance d’un
serveur en dehors de votre zone de confiance intranet tels que ceux venant d’Internet seront quant à eux bloqués.
Pour en savoir plus sur la sécurité en général, mais aussi sur les autres stratégies d’exécution disponibles, veuillez
vous reporter au chapitre Sécurité.
4. La directive #Requires
La directive #Requires permet de s’assurer de l’existence de certains prérequis afin de permettre l’exécution d’un
script.
Ainsi il est possible de s’assurer :
l Que PowerShell s’exécute dans une version minimale,
l Que PowerShell s’exécute dans une édition spécifique (Desktop ou Core),
l Que PowerShell s’exécute en mode Administrateur,
l De la présence de certains modules et snapins ainsi que de leur numéro de version.
Les différents cas d’usages sont les suivants :
Exemple :
Soit le début du script suivant :
#Requires -Version 5
#Requires -Modules PSWorkflow,
@{ModuleName="DataOnTap";ModuleVersion=3.2.0.0}
#Requires -PSSnapin DiskSnapin -Version 1.2
#Requires -RunAsAdministrator
Nous avons volontairement forcé un peu le trait, car il est rare de vérifier autant de prérequis à la fois, cependant
sachez que vous pouvez spécifier plusieurs directives #Requires dans un même script. Les directives doivent
systématiquement apparaître en début de ligne et commencer par un dièse.
Les numéros de version que nous avons spécifiés tant pour le snapin que pour le module sont facultatifs.
5. Prise de conscience de l’environnement d’exécution (contexte)
Certaines informations peuvent se révéler précieuses lors de l’exécution d’un script, telles que :
l l’emplacement du script courant,
l le nom du script courant,
l la ligne de commandes complète qui a permis l’exécution du script,
l etc.
Lorsque nous développons des scripts d’une certaine envergure, il est souvent nécessaire de générer des fichiers
de logs, et pouvoir « logguer » la ligne de commandes complète (avec les paramètres) ayant permis l’exécution d’un
script est parfois d’une importance capitale.
Pour ce faire, PowerShell met à notre disposition de nombreuses variables automatiques. Cellesci sont récapitulées
dans le tableau cidessous :
Variable Description
$PSCommandPath Contient le chemin complet du script courant.
$PSScriptRoot Contient le répertoire dans lequel se trouve le script
courant.
$MyInvocation.PSScriptRoot Contient le répertoire dans lequel se trouve le script
parent ayant permis d’appeler un script enfant (cet
objet ne contient une valeur que si l’appel au script
enfant s’effectue au travers d’un script parent).
Comme vous pouvez le voir, il n’est pas évident de s’y retrouver étant donné toutes les variables à notre
disposition. Des exemples valant mieux qu’un long discours, créons le petit script suivant que nous appellerons
testInvocation.ps1 et que nous stockerons dans l’emplacement C:\Temp :
# testInvocation.ps1
Param ($a, $b, $c)
"`$PSCommandPath = $PSCommandPath"
"`$PSScriptRoot = $PSScriptRoot"
"`$MyInvocation.MyCommand.Path = $($MyInvocation.MyCommand.Path)"
"`$MyInvocation.MyCommand.line = $($MyInvocation.line)"
"`$MyInvocation.PSCommandPath = $($MyInvocation.PSCommandPath)"
"`$MyInvocation.PSScriptRoot = $($MyInvocation.PSScriptRoot)"
$MyInvocation | Format-List * -force
Ce script va nous montrer le contenu de chacune de ces variables et cela va nous aider à mieux comprendre le rôle
de ces dernières.
À présent, positionnonsnous dans un autre répertoire que le répertoire où se trouve le script, par exemple dans le
répertoire C:\Program Files, et exécutons le script testInvocation.ps1 de la façon suivante :
PS > ..\Temp\testinvocation.ps1 -a ’Bonjour’ -b 22
$PSCommandPath = C:\Temp\testInvocation.ps1
$PSScriptRoot = C:\Temp
$MyInvocation.MyCommand.Path = C:\Temp\testInvocation.ps1
$MyInvocation.MyCommand.line = ..\Temp\testinvocation.ps1 -a
’Bonjour’ -b 22
$MyInvocation.PSCommandPath =
$MyInvocation.PSScriptRoot =
MyCommand : testInvocation.ps1
BoundParameters : {[a, Bonjour], [b, 22]}
UnboundArguments : {}
ScriptLineNumber : 1
OffsetInLine : 1
HistoryId : 194
ScriptName :
Line : ..\Temp\testinvocation.ps1 -a ’Bonjour’ -b 22
PositionMessage : At line:1 char:1
+ ..\Temp\testinvocation.ps1 -a ’Bonjour’ -b 22
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PSScriptRoot :
PSCommandPath :
InvocationName : ..\Temp\testinvocation.ps1
PipelineLength : 1
PipelinePosition : 1
ExpectingInput : False
CommandOrigin : Runspace
DisplayScriptPosition :
On se rend immédiatement compte que l’objet $MyInvocation.MyCommand.Path contient l’emplacement
absolu complet du script courant et que, mieux encore, l’objet $MyInvocation.MyCommand.line contient la
ligne de commandes qui a permis l’exécution du script.
Voyons ce qu’il se passe maintenant si nous créons un second script dont le rôle est d’exécuter
testInvocation.ps1. Nous appellerons ce script lanceur.ps1 et nous l’enregistrerons dans le répertoire
Documents.
# lanceur.ps1
& C:\Temp\testinvocation.ps1 -a 2 -b 1
À présent, positionnonsnous à la racine du disque C:\ et tapons la commande
C:\Users\Administrateur\Documents\lanceur.ps1 pour exécuter le script, tel que cidessous :
PS > C:\Users\Administrateur\Documents\lanceur.ps1
$PSCommandPath = C:\Temp\testinvocation.ps1
$PSScriptRoot = C:\Temp
$MyInvocation.MyCommand.Path = C:\Temp\testinvocation.ps1
$MyInvocation.MyCommand.line = & C:\Temp\testinvocation.ps1 -a 2 -b 1
$MyInvocation.PSCommandPath = C:\Users\Administrator\Documents\lanceur.ps1
$MyInvocation.PSScriptRoot = C:\Users\Administrator\Documents
MyCommand : testinvocation.ps1
BoundParameters : {[a, 2], [b, 1]}
UnboundArguments : {}
ScriptLineNumber : 2
OffsetInLine : 1
HistoryId : 15
ScriptName : C:\Users\Administrator\Documents\lanceur.ps1
Line : & c:\Temp\testinvocation.ps1 -a 2 -b 1
PositionMessage : At C:\Users\Administrator\Documents\lanceur.ps1:2
char:1
+ & c:\Temp\testinvocation.ps1 -a 2 -b 1
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PSScriptRoot : C:\Users\Administrateur\Documents
PSCommandPath : C:\Users\Administrateur\Documents\lanceur.ps1
InvocationName : &
PipelineLength : 1
PipelinePosition : 1
ExpectingInput : False
CommandOrigin : Internal
DisplayScriptPosition :
6. Internationalisation
Dans la version 1 de PowerShell, l’édition de scripts destinés à un public constitué de personnes de nationalités
différentes n’était pas chose évidente car le scripteur se devait de traduire manuellement tout le contenu textuel de
ses scripts. Dans PowerShell v2 est apparu ce qu’on appelle « l’internationalisation de script » et cette fonctionnalité
n’a pas évolué avec les versions suivantes de PowerShell. Le principe de l’internationalisation est de permettre
l’écriture de scripts en différentes langues sans pour autant modifier le code contenu dans les scripts. Par exemple,
si vous créez un script et que celuici doit être exécuté par plusieurs personnes, chacune utilisant une version de
PowerShell différente en termes de langue (variable $PSUICulture différente), le fait d’utiliser
l’internationalisation permettra aux utilisateurs d’obtenir toute la partie textuelle du script dans leur langue, et ce,
sans aucune manipulation de leur part. Regardons à présent comment cela fonctionne.
La première précaution à prendre est, bien évidemment, de séparer les données (data) du code contenu dans le
script.
Pour ce faire, nous utilisons une section Data. Et c’est à l’intérieur de cette section que nous utilisons la
commandelette ConvertFrom-StringData, qui va créer une table de hachage des chaînes de caractères.
Exemple
$TexteScript = Data {
ConvertFrom-StringData @’
Message_1 = Bonjour
Message_2 = Entrez une valeur
Message_3 = Entrez une autre valeur
Message_4 = Le résultat de l’addition de ces deux valeurs est :
’@
}
Notez que nous utilisons ici une Here-String. Une Here-String commence avec un séparateur @’ et se
termine par ’@ (le dernier séparateur doit absolument être précédé d’un retour chariot). Tous les caractères entre
les délimiteurs arobase sont considérés comme du texte.
De façon à permettre la traduction des sections Data, une traduction en différentes langues doit être sauvegardée
dans des fichiers .psd1 et sous une arborescence particulière.
Les fichiers .psd1 se doivent d ’être enregistrés dans un sousrépertoire au format <langage>-<Pays>, comme
l’indique la variable $PSUICulture. Ainsi, si le script principal nommé MonScript.ps1 se trouve dans le
répertoire C:\Temp, les fichiers MonScript.psd1 se trouveront sous l’arborescence suivante :
Exemple de contenu des fichiers .psd1
Fichier C:\Temp\enUS\MonScript.psd1 :
ConvertFrom-StringData @’
Message_1 = Hello
Message_2 = Enter a value
Message_3 = Enter a second value
Message_4 = The result of the addition of these two values is:
’@
Fichier C:\Temp\esES\MonScript.psd1 :
ConvertFrom-StringData @’
Message_1 = Hola
Message_2 = Introducid un valor
Message_3 = Introducid un segundo valor
Message_4 = El resultado de la adición de estos dos valores
es la siguiente:
’@
Enfin, dernière étape, permettre l’importation des chaînes de caractères dans la langue de l’interface utilisateur via
la commandelette Import-LocalizedData. L’utilisation de cette commandelette importe le fichier .psd1
correspondant à la langue utilisée, et rend transparentes les actions de traduction des éléments textuels du script.
Le script complet devient le suivant :
$TexteScript = Data {
#Culture fr-FR
ConvertFrom-StringData @’
Message_1 = Bonjour
Message_2 = Entrez une valeur
Message_3 = Entrez une autre valeur
Message_4 = Le résultat de l’addition de ces deux valeurs est :
’@
}
Import-LocalizedData TexteScript
# Début du script
Write-Host $TexteScript.Message_1
Write-Host $TexteScript.Message_2
[int]$var1 = Read-Host
Write-Host $TexteScript.Message_3
[int]$var2 = Read-Host
$resultat = $var1 + $var2
Write-Host "$($TexteScript.Message_4) $resultat"
# Fin du script
En exécutant le script précédent sur un poste Windows version US, nous obtenons le résultat suivant :
PS > C:\Temp\MonScript.ps1
Hello
Enter a value
5
Enter a second value
6
The result of the addition of these two values is: 11
La gestion de fichiers
La gestion des fichiers est très simple avec PowerShell, contrairement à VBScript, pour ceux qui ont eu l’occasion de le
pratiquer. En effet, il n’est plus question d’instancier des objets COM de type filesystem, de les ouvrir en
spécifiant un mode d’accès (lecture ou écriture), puis de les fermer. PowerShell apporte un jeu de commandelettes
dédié à la gestion de fichiers et nous verrons que cela représente un énorme gain de productivité dans l’écriture des
scripts.
Cela étant, il y a un point particulier sur lequel il est important d’être vigilant et de savoir ce que l’on fait si l’on veut
s’éviter bien des problèmes. Il s’agit du format d’encodage des fichiers texte. Ils sont nombreux et il ne faut pas se
tromper ; nous détaillerons donc ce point dans ce présent chapitre.
Dans le chapitre À la découverte de PowerShell, nous nous étions intéressés au contenant, c’estàdire au fichier lui
même, et nous avions vu comment les créer, les déplacer, les renommer, etc. À présent, intéressonsnous au
contenu, et voyons entre autres comment le générer et comment le relire.
1. Formats d’encodage des fichiers texte
Avant d’entrer dans le vif du sujet, il nous paraît nécessaire de prendre quelques minutes pour faire le point sur ce
vaste et important sujet qu’est l’encodage des fichiers texte.
Sans entrer dans les détails de la préhistoire de l’informatique, commençons avec le standard ASCII, bien connu de
tous. Celuici, apparu avec les premiers ordinateurs à la fin des années 60 aux ÉtatsUnis, encode les caractères sur
7 bits (de la position 0 à 127), soit un total de 128 caractères que l’on a regroupés dans une table qui fut devenue
célèbre, c’est la fameuse table ASCII. Le souci de cette norme, pour nous Français (mais pas seulement), c’est
qu’elle ne contient pas nos chers caractères accentués… Ce qui est logique puisque l’ASCII a été inventé par un
organisme américain pour les Américains.
L’ASCII étendu (ANSI), une solution intermédiaire
Comme à tout problème, toute solution, et qu’en informatique les processeurs travaillent sur des puissances de 2, il
n’était pas possible d’écrire un caractère autrement que sur 8 bits (1 octet). Il y avait donc un bit inutilisé dans
l’encodage des caractères au format ASCII. Les informaticiens de l’époque ont alors décidé d’utiliser ce dernier bit
pour étendre la table ASCII afin de pouvoir y stocker les caractères accentués des différentes langues. Cette
technique a donné naissance au format ASCII étendu, également connu sous l’appellation ANSI. Il fut donc créé ce
que l’on appela des « pages de codes » afin de stocker les caractères accentués de la langue française, espagnole,
allemande, et autres. Bien que relativement efficace, cette solution a ajouté un peu de complexité de configuration
car, en fonction de la langue, il fallait choisir la page de codes adéquate.
Le monde a fonctionné ainsi durant de nombreuses années jusqu’au début des années 1990 et l’arrivée de
l’Internet, qui a tout révolutionné en repoussant nos frontières. En effet, il a fallu trouver des moyens pour que les
ordinateurs soient capables de prendre en charge plusieurs langues simultanément. En effet, qui aurait accepté que
les caractères accentués français ne s’affichent pas correctement sur un système d’exploitation anglais, espagnol ou
chinois et réciproquement !? C’est alors que le standard Unicode a fait son apparition et que les choses se sont
corsées d’avantage d’un point de vue technique…
L’Unicode et ses variantes
Unicode s’est très rapidement imposé comme une solution efficace aux problèmes d’encodage. Seulement voilà, il
existe d’innombrables formats Unicode ainsi que de nombreuses variantes. Nous avons mis dans le tableau ciaprès
les formats les plus courants que nous serons à même de manipuler avec PowerShell :
Format Unicode Type Poids d’un caractère
d’encodage
UTF8 8 bits 8 bits pour un caractère « standard », variable pour les caractères
accentués.
On peut également trouver de l’UTF7, mais ce format n’a jamais fait partie officiellement du standard. De plus, pour
nous Français, il n’a aucun intérêt dans la mesure où nous ne pouvons pas l’utiliser pour encoder nos accents.
Quant à l’UTF16 et l’UTF32, vous l’aurez compris, ils nécessitent respectivement 2 et 4 octets pour stocker un
caractère, ce qui fera indubitablement doubler ou quadrupler la taille des fichiers encodés dans ces formats par
rapport à de l’ASCII pur ou à de l’ASCII étendu.
Quant à l’UTF8, cet encodage est intéressant car tous les caractères standards, c’estàdire ceux de la table ASCII,
sont encodés sur 8 bits, tandis que les caractères spéciaux (tous les autres) peuvent être encodés sur deux à
quatre octets (ce qui laisse de la marge). Ce format est donc assez bien équilibré car il permet d’encoder la plupart
des caractères de la planète tout en gardant une taille compacte.
Gros Indien ou petit Indien ?
Revenons un instant sur les formats UTF16 et UTF32. Ceuxci encodant les caractères sur plusieurs octets, se pose
alors inévitablement la question de l’ordre de disposition des octets. Fautil écrire l’octet de poids fort avant l’octet
de poids faible ou l’inverse ? C’est un peu comme si tous les humains de la planète parlaient une langue commune
mais que certains pays écrivaient cette langue sur papier de la droite vers la gauche alors que d’autres l’écrivaient
de la gauche vers la droite. En informatique, cette notion qui définit l’ordre d’encodage des octets des caractères
s’appelle l’endianness ; il n’y a pas à notre connaissance de terme francophone correspondant.
On distingue deux variantes d’encodage Unicode que sont Little Endian et Big Endian. La plupart des OS actuels
(Windows, Linux, Mac OS) ont adopté la norme Little Endian, mais certains travaillent encore avec Big Endian (HP
UX, AIX, iSeries, zSeries, etc.).
Byte Order Mark (BOM) des fichiers Unicode
Enfin, pour clore cette mise au point sur les différents formats d’encodage des fichiers texte, il ne nous reste plus
qu’à aborder la notion de Byte Order Mark (BOM), souvent traduit par Indicateur d’Ordre des Octets (IOO).
Le BOM est une suite de quelques octets qui permet d’indiquer le format d’encodage d’un fichier texte. Le BOM,
lorsqu’il est présent, se place toujours au début d’un fichier. Il permet d’indiquer aux programmes tels que les
éditeurs de texte dans quel format se trouve un fichier afin de l’ouvrir correctement.
Format Unicode Byte Order Mark
UTF8 EF BB BF
UTF16 FF FE
UTF32 FF FE 00 00
Afin d’alléger le tableau, les BOM indiqués ici le sont pour la variante Little Endian, étant celle par défaut sur un
système Windows.
Souvenezvous que la notion d’Endianness ne s’applique qu’aux formats Unicode UTF16 et UTF32.
Pour visualiser le BOM d’un fichier texte, nous pouvons faire appel à la commande Format-Hex.
Exemple : BOM d’un fichier UTF8
Path: C:\temp\UTF8BOM.txt
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000 EF BB BF C3 89 2D C3 B9 2D C3 A8 2D C3 A9 0D 0A É-ù-è-é..
La commande Format-Hex est extrêmement pratique car elle permet de lire le contenu de n’importe quel type de
fichiers. Elle se comporte un peu à la manière d’un éditeur héxadécimal, sauf qu’on ne peut pas éditer le contenu.
On pourrait donc l’assimiler à un visualiseur héxadécimal.
Essayonsla avec un autre type de fichier, tel qu’un fichier PDF.
Path: C:\temp\MANUEL_V2.pdf
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000 25 50 44 46 2D 31 2E 36 0D 25 E2 E3 CF D3 0D 0A %PDF-1.6.%âãÏÓ..
00000010 34 34 32 36 20 30 20 6F 62 6A 20 3C 3C 2F 4C 69 4426 0 obj <</Li
00000020 6E 65 61 72 69 7A 65 64 20 31 2F 4C 20 38 39 36 nearized 1/L 896
00000030 37 38 30 31 2F 4F 20 34 34 33 31 2F 45 20 31 31 7801/O 4431/E 11
00000040 38 30 38 38 2F 4E 20 31 33 38 2F 54 20 38 38 37 8088/N 138/T 887
00000050 39 32 31 35 2F 48 20 5B 20 34 34 30 36 20 33 32 9215/H [ 4406 32
00000060 33 38 5D 3E 3E 0D 65 6E 64 6F 62 6A 0D 20 20 20 38]>>.endobj.
00000070 20 20 0D 0A 78 72 65 66 0D 0A 34 34 32 36 20 32 ..xref..4426 2
00000080 30 30 0D 0A 30 30 30 30 30 30 30 30 31 36 20 30 00..0000000016 0
00000090 30 30 30 30 20 6E 0D 0A 30 30 30 30 30 30 37 38 0000 n..00000078
Notez que pour limiter le nombre de lignes de résultat, nous avons fait appel à la commande Select-Object.
Il faut savoir que les quelques premiers octets d’un fichier binaire permettent toujours d’identifier le
type d’un fichier. C’est sa signature. On appelle cela également le « nombre magique » ou « magic
number » en anglais. Dans le cas d’un fichier PDF, sa signature est la suite d’octets 25, 50, 44, 46.
2. Différences Windows PowerShell 5.x / PowerShell Core
Par le passé, l’équipe de développement de PowerShell n’avait pas été très rigoureuse quant à l’encodage des
fichiers générés par les différentes commandes PowerShell qui produisent des fichiers texte. Par exemple, par
défaut avec Windows PowerShell 5.x, la commande Out-File encode les fichiers en UTF16 tandis que la
commande Export-CSV les crée en UTF8 alors que la commande Set-Content les génère en ANSI. Cette
disparité dans les formats d’encodage a créé beaucoup de confusion et donné du fil à retordre à de nombreux
scripteurs… Heureusement, cette époque est révolue ! À présent, PowerShell Core corrige les erreurs du passé et
tous les fichiers générés par les différentes commandes le sont en Unicode UTF8 sans BOM.
Pourquoi sans BOM, nous direzvous ? Eh bien tout simplement parce que la plupart des plateformes autres que
Windows utilisent ce format d’encodage par défaut. PowerShell Core étant multiplateforme, c’est donc ce format qui
l’a emporté. De plus, la plupart des outils et applications Windows migrent peu à peu vers cet encodage.
Dernier point de vigilance : faites très attention lorsque vous utilisez le paramètre -Encoding car les valeurs
peuvent légèrement différer d’une commande à l’autre avec Windows PowerShell. De plus, entre PowerShell Core et
Windows PowerShell, pour ce même paramètre les valeurs ne sont pas exactement les mêmes. Nous entrons donc
ici dans une zone où il peut donc y avoir potentiellement des problèmes de compatibilité entre ces deux éditions de
PowerShell. La prudence est donc de mise…
3. Bonnes pratiques
Prenez garde au format d’encodage de vos fichiers texte ! Ce précieux conseil vous évitera bien des maux de tête.
D’une manière générale, lorsque vous créez un fichier avec quelque commande que ce soit, nous vous
recommandons de toujours préciser son encodage grâce au paramètre -Encoding. Ce conseil prend tout son
sens avec Windows PowerShell (voir partie précédente) !
En termes de choix du format d’encodage, le format UTF8 constitue une option intéressante car il est compact et
permet d’encoder tous les caractères spéciaux de la langue française (mais pas seulement).
4. Écriture de données dans un fichier
Il y a deux façons essentielles de procéder pour écrire des données dans un fichier. Nous pouvons utiliser soit Set-
Content, soit Out-File.
Bien que ces deux commandes servent à faire la même chose : créer des fichiers et des données, il y a cependant
une différence notable qu’il est important de connaître mais qui n’est pas facilement décelable.
Lorsque Out-File est utilisée, cette commande tente, tout comme les autres commandes Out-*, de formater le
flux avant de l’écrire dans le fichier.
Set-Content quant à elle, ne cherche pas à formater le flux mais elle lui applique seulement la méthode
ToString() afin d’être sûre d’écrire des caractères. C’est cela la principale différence. Cependant, bien qu’elle
puisse sembler anodine, vous aurez des surprises si vous tentez d’écrire un objet dans un fichier avec Set-
Content sans l’avoir formaté au préalable.
Par exemple, le résultat de cette commande écrit dans un fichier le nom du type de l’objet au lieu de son contenu :
System.Diagnostics.Process (powershell)
Alors que la commande suivante nous donne le résultat attendu :
Pour obtenir le même résultat avec Set-Content, il aurait fallu effectuer un transtypage préalable sur l’objet
avant de l’écrire, comme ceci :
Out-String permet de convertir les objets émis en les représentant sous forme de chaîne. Le paramètre -
Stream permet d’envoyer au pipeline autant de chaînes que d’objets reçus, au lieu d’envoyer une chaîne unique
contenant la représentation de tous les objets.
Si nous souhaitons personnaliser le résultat, nous pourrions écrire ceci :
Une autre différence est que Out-File formate la sortie telle qu’elle est affichée dans la console. C’estàdire que
Out-File ajoute les retours chariot et des sauts de ligne en fonction de la largeur de la fenêtre PowerShell, ce qui
n’est pas le cas de Set-Content.
En résumé, on aura plutôt tendance à privilégier l’utilisation de Out-File avec les fichiers texte, et à utiliser Set-
Content avec les fichiers binaires.
a. Fichiers texte avec OutFile
Cette commande très puissante permet de créer des fichiers avec du contenu. Elle fait sensiblement la même
chose que les opérateurs de redirection, sauf que l’on peut spécifier à Out-File un certain nombre de
paramètres, chose que l’on ne peut pas faire avec les opérateurs de redirection.
Voici la liste des paramètres les plus couramment utilisés :
Paramètre Description
Les valeurs possibles pour le paramètre d’encodage -Encoding sont les suivantes :
Nom Description
Ascii Force l’encodage en ASCII de base (jeu de caractères 0 à 127, 7 bits).
Attention, ce type ne contient pas les caractères accentués. Pour cela,
préférez la valeur Default ou un format Unicole.
Oem Utilise l’identificateur de la page de codes du fabricant d’ordinateurs OEM
(Original Equipment Manufacturer) actuel pour le système d’exploitation.
L’astérisque signifie que cette valeur n’est présente qu’avec PowerShell Core.
Exemple
Création d’un fichier ASCII contenant des informations sur un processus du système.
Exemple 2
Ajout de données à un fichier existant.
Lorsque vous ajoutez des données à un fichier texte, n’oubliez jamais de tenir compte de l’encodage de celuici,
sous peine de rendre votre fichier illisible. Une méthode simple quand vous ne connaissez pas l’origine d’un fichier
et que vous avez des données à lui ajouter, est de l’ouvrir dans le blocnotes et de faire comme si vous vouliez
l’enregistrer en choisissant l’option Enregistrer sous. Ainsi dans le bas de la fenêtre, vous pourrez voir une liste
déroulante nommée Codage vous permettant de choisir l’encodage désiré, sachant que le choix proposé par défaut est
celui du fichier que vous avez ouvert.
Visual Studio Code affiche également dans la barre de statut (celle du bas) le format d’encodage du fichier
courant.
b. Redirection du flux standard
Création de fichiers
Nous avons vu dans le chapitre concernant les opérateurs qu’il existait un opérateur de redirection, l’opérateur
supérieur à >. Cet opérateur représente la forme la plus simple pour créer un fichier. Il fonctionne à l’identique que
sous CMD.exe (à l’exception près du type d’encodage par défaut qui est Unicode). À savoir que lorsqu’il est
utilisé de façon classique, seul le flux de sortie standard est redirigé dans un fichier.
Exemple
Cette ligne de commandes liste les fichiers et dossiers contenus dans le répertoire C:\Temp dans le fichier
dir.txt.
Pas de changement donc pour les habitués du CMD.exe, pour le fonctionnement de cet opérateur.
Ajout de données à un fichier
Pas de changement non plus pour l’ajout de données, qui se réalise toujours avec l’opérateur de redirection >>.
Ainsi, grâce à cet opérateur, nous pouvons ajouter du contenu à la fin d’un fichier existant.
Exemple
Cette ligne de commandes aura pour effet d’ajouter la date courante à la fin du fichier dir.txt, et ce tout en
préservant le contenu présent à l’intérieur du fichier.
Les opérateurs de redirection de flux > et >> font en réalité appel à la commandelette Out-File. Pour en avoir
le cœur net, appelons à la rescousse Trace-Command (que nous détaillerons dans un prochain chapitre) pour
tenter de découvrir ce qu’il y a à l’intérieur de la bête...
Essayons ceci :
c. Création de fichiers binaires avec SetContent
Il ne faut pas oublier que Set-Content fait partie de la famille des commandelettes *-Content, qui contient :
l Add-Content : ajoute des données à un fichier existant.
l Clear-Content : efface les données présentes dans un fichier, mais pas le fichier.
l Get-Content : lit le contenu d’un fichier. Nous étudierons cette commandelette en détail un peu plus loin.
Voici les paramètres de Set-Content :
Paramètre Description
-Filter <String> Spécifie un filtre dans le format ou le langage du
fournisseur.
-PassThru <Switch> Transmet l’objet traité par cette commande au pipeline
après l’exécution.
Les valeurs possibles pour le paramètre d’encodage -Encoding sont les suivantes :
Nom Description
L’astérisque signifie que cette valeur n’est présente qu’avec PowerShell Core.
Deux astérisques signifient que cette valeur n’est pas disponible avec PowerShell Core.
IMPORTANT : faites bien attention car les valeurs de ce paramètre ne sont pas tout à fait les mêmes que pour la
commande Out-File.
Bien qu’il soit quand même possible d’écrire des données textuelles avec Set-Content, le plus intéressant est la
possibilité d’écrire directement des octets dans un fichier quel que soit son type.
Si vous envoyez des données de type String dans un fichier sans spécifier explicitement l’encodage désiré, le
fichier résultant sera un fichier ANSI. C’estàdire un fichier ASCII étendu avec votre page de code courante
pour prendre en compte les caractères accentués.
Exemple
Envoi de données textuelles dans un fichier.
Directory : C:\Temp
Pourquoi diable avonsnous un fichier de 7 octets alors que nous n’avons envoyé que cinq caractères à l’intérieur ?
Appelons Format-Hex à la rescousse afin de prendre connaissance avec exactitude du contenu de notre fichier.
Ceci est tout à fait normal car sur la plateforme Windows (c’était déjà le cas sous DOS), chaque ligne d’un fichier
texte se termine toujours par CR et LF. Alors que sous Unix (et autres dérivés) une ligne se termine uniquement
par LF. C’est ce qui explique pourquoi il y a quelques problèmes de mise en forme lorsque l’on échange des fichiers
texte entre ces platesformes...
Dans l’exemple qui va suivre, nous allons tenter d’écrire une chaîne de caractères dans un fichier mais cette foisci
nous ferons en sorte que les caractères de contrôle CR et LF ne soient pas ajoutés en fin de ligne. Pour ce faire,
nous enverrons les octets correspondants aux codes ASCII de la chaîne à écrire ; puis nous spécifierons
l’encodage byte pour Set-Content.
Exemple
Écriture d’un flux d’octets dans un fichier sans CR LF.
En faisant cela, nous convertissons la chaîne AAéBB en un tableau de caractères, que nous convertissons une
seconde fois en un tableau d’octets. Nous passons ensuite le tout à Set-Content où nous prenons bien soin
d’ajouter le paramètre -Encoding byte.
Conversion de fichiers Unix en fichiers Windows (Unix2DOS)
Dans le dernier exemple qui va suivre, nous allons créer un petit convertisseur de fichiers texte au format Unix en
format DOS/Windows.
Exemple
Convertir un fichier texte Unix en DOS.
# Convert-Unix2Dos.ps1
[CmdletBinding()]
Param (
[parameter(Mandatory=$true)]
[string]$path,
[parameter(Mandatory=$false)]
[string]$destination = $path
)
On stocke le contenu du fichier source sous forme d’une suite d’octets dans le tableau $tab. Après, on parcourt
l’intégralité du tableau $tab à la recherche du caractère LF. Lorsqu’on en trouve un, on concatène le début de
notre tableau avec CR et la fin de notre tableau, puis on réinjecte le nouveau contenu dans notre tableau $tab.
En somme, nous écrasons à chaque itération le contenu de $tab par un nouveau contenu modifié. Nous faisons
ceci car il n’existe pas de méthode pour insérer un élément dans un tableau à un emplacement donné. Enfin, nous
incrémentons notre variable d’indice d’une position car nous avons ajouté un élément dans $tab ; sans quoi le
test serait toujours vrai et nous tomberions dans une boucle infinie. Enfin, pour l’écrire, notre tableau d’octets est
passé via le pipeline à Set-Content sans oublier de spécifier le type d’encodage byte.
5. Lecture de données avec GetContent
Comme vous vous en doutez et comme son nom l’indique Get-Content va nous permettre de lire le contenu d’un
fichier. Ce dernier peut être soit de type texte, soit de type binaire, peu importe. Get-Content s’en accommode à
partir du moment où on lui précise l’encodage correspondant. Par défaut cette commande s’attend à lire des fichiers
texte.
Voici les paramètres de Get-Content :
Paramètre Description
-ReadCount <Int64> Nombre de lignes de contenu envoyées simultanément au
pipeline. Par défaut elles sont envoyées une par une (valeur
1). Une valeur de 0 indique qu’on veut envoyer toutes les
lignes d’un coup.
-Raw <Switch> Ignore le délimiteur standard et retourne le fichier d’un seul
bloc (en un objet unique) au lieu de retourner un tableau de
chaînes de caractères.
-Wait <Switch> Récupère les nouvelles lignes de texte ajoutées au fichier
(vérifie chaque seconde) et attend indéfiniment jusqu’à ce
que l’utilisateur presse [Ctrl] C.
Les valeurs possibles pour le paramètre d’encodage sont les suivantes :
Nom Description
ASCII Force l’encodage en ASCII de base (jeu de caractères 0 à 127, 7 bits). Les
caractères accentués ne font pas partie du jeu de caractères.
L’astérisque signifie que cette valeur n’est présente qu’avec PowerShell Core.
Deux astérisques signifient que cette valeur n’est pas disponible avec PowerShell Core.
Exemple
Fonctionnalités de base.
Dans cet exemple, nous créons un fichier texte avec l’opérateur de redirection « supérieur à » (Unicode, donc)
contenant la date, l’heure ainsi que la liste des processus en cours d’exécution. Puis, nous faisons appel à Get-
Content pour lire les dix premières lignes du fichier.
Manipuler un fichier comme un tableau
Lorsque l’on utilise Get-Content, nous obtenons en retour un tableau de chaînes de caractères. Comme il s’agit
d’un tableau, il devient alors très facile de manipuler son contenu ou d’aller lire une ligne spécifique.
Exemple : lecture de la 15e ligne d’un fichier
Nous recevons le contenu du fichier dans la variable $fic, puis nous récupérons la ligne située à l’indice 14 du
tableau (en réalité la 15 e ligne du fichier car n’oubliez pas que les indices de tableau commencent à zéro).
De plus, comme une chaîne est également un tableau de caractères, on peut lire n’importe quel caractère en
utilisant la syntaxe des tableaux à deux dimensions. Par exemple, le i du mot fourmi se trouvant à l’index 8 :
PS > $fic[14][8]
i
PS > $fic.Length
22
22 est le nombre de lignes de notre fichier.
Lecture d’un fichier en mode « brut »
Comme nous vous le disions en introduction de cette commande, Get-Content sait lire des octets. Cette
fonctionnalité est particulièrement intéressante pour révéler le contenu réel des fichiers, c’est en quelque sorte un
mode d’accès de bas niveau au contenu.
En effet, qu’estce qui différencie un fichier texte d’un fichier binaire ? La réponse est simplement : le contenu ou
l’interprétation de celuici. Dans les deux cas, un fichier possède des attributs qui caractérisent tels qu’un nom, une
extension, une taille, une date de création, etc.
Un fichier texte contient, tout comme son homologue le fichier binaire, une suite d’octets possédant une certaine
structure.
Essayons d’ouvrir en mode brut un fichier texte Unicode, mais auparavant créons un nouveau fichier :
255 254 80 0 111 0 119 0 101 0 114 0 83 0 104 0 101 0 108 0 108 0 13 0 10 0
Les octets s’affichent en réalité verticalement, mais pour faciliter la lecture et la compréhension de l’exemple nous
les avons retranscrits horizontalement.
Afin d’afficher le résultat sur une seule ligne, nous aurions pu écrire ceci :
Dans ce contexte, l’opérateur -join réalise une concaténation des éléments du tableau avec le
caractère espace.
Un œ il averti avec les fichiers texte remarquerait les deux choses suivantes :
l Le fichier débute par deux octets remarquables : 255 et 254.
l Tous les caractères sont codés sur deux octets dont l’un des deux vaut zéro.
La présence des octets 255 et 254 s’explique par le fait que tout fichier Unicode commence par un entête (le
BOM dont nous avons parlé au début de ce présent chapitre) dont la longueur varie entre 2 et 4 octets. Cela diffère
selon le codage Unicode choisi (UTF8, UTF16, UTF32).
La présence des zéros s’explique par le fait que dans un fichier UFT16 tous les caractères sont codés sur deux
octets.
Exemple : Déterminer le type d’encodage d’un fichier
La question que nous nous posions déjà depuis quelques pages, à savoir : « comment reconnaître le type
d’encodage d’un fichier texte ? » a, enfin trouvé sa réponse dans l’exemple précédent. Les premiers octets d’un
fichier texte nous donnent son encodage.
Réalisons donc un petit script utilitaire qui nous dira de quel type est l’encodage d’un fichier à partir de ses premiers
octets.
# Get-FileTypeEncoding.ps1
[CmdletBinding()]
Param (
[parameter(Mandatory=$true)]
[string]$path
)
Ce script lit les quatre premiers octets du fichier, les met en forme (conversion hexadécimale au format texte) et les
compare à la signature Unicode pour déterminer le type d’encodage. Si aucune signature n’a été trouvée, c’est
que le fichier est soit de type ASCII pur (caractères US de 0 à 127), soit de type ANSI (ASCII étendu, soit ANSI
+ page de codes pour gérer les caractères accentués).
Cet exemple n’est pas parfait car il ne prend pas en compte les fichiers encodés en UTF8 sans BOM. Pour ce faire, il
aurait fallu ajouter une vérification complémentaire, sorte d’analyse heuristique du contenu, car pas de BOM signifie
pas d’entête. Il est donc plus difficile de déterminer avec certitude le format d’encodage d’un fichier.
Le format UTF8NoBOM est cependant le format privilégié de PowerShell Core, et ce pour des raisons
d’interopérabilité avec les autres plateformes et donc d’homogénéité de fonctionnement.
6. Recherche de contenu dans un fichier avec SelectString
Grâce à Select-String nous pouvons passer en revue le contenu d’une chaîne de caractères, d’un fichier, ou
d’un grand nombre de fichiers à la recherche d’une chaîne de caractères sous forme d’expression régulière. Les
"Unixiens" connaissant la commande Grep ne seront pas dépaysés car Select-String se veut être plus ou
moins son équivalent.
Voici les paramètres de Select-String :
Paramètre Description
-List <Switch> Spécifie qu’une seule correspondance doit être retournée pour
chaque fichier d’entrée.
-Context <Int32> Permet de sélectionner un nombre spécifique de lignes avant et
après la ligne contenant la correspondance (permettant ainsi de
voir le contenu recherché dans son contexte).
Les caractères accentués ne sont pas pris correctement en compte dans les recherches à l’intérieur des fichiers
ANSI. Par contre, tout fonctionne correctement avec les fichiers Unicode.
Exemple
Recherche simple.
Dans cet exemple, nous recherchons la chaîne « fourmi » parmi tous les fichiers texte du répertoire C:\Temp.
Nous obtenons en retour le nom des fichiers (ou du fichier s’il n’y en avait eu qu’un seul) qui contiennent la chaîne
recherchée. Les valeurs 8, 15 et 1 correspondent au numéro de la ligne dans le fichier où une occurrence a été
trouvée.
Regardons quel serait le résultat avec -List :
PS > Select-String -Path C:\Temp\*.txt -Pattern ’fourmi’ -List
Les résultats obtenus sont de type Microsoft.PowerShell.Commands. MatchInfo. Ainsi, il est possible
d’obtenir et de manipuler un certain nombre d’informations complémentaires en passant par une variable
intermédiaire, comme ceci :
TypeName: Microsoft.PowerShell.Commands.MatchInfo
À présent, essayons de forcer un affichage sous forme de liste :
IgnoreCase : True
LineNumber : 8
Line : Chez la fourmi sa voisine,
Filename : CigaleFourmi.txt
Path : C:\Temp\CigaleFourmi.txt
Pattern : fourmi
Context :
Matches : {Fourmi}
IgnoreCase : True
LineNumber : 15
Line : La fourmi n’est pas prêteuse ;
Filename : CigaleFourmi.txt
Path : C:\Temp\CigaleFourmi.txt
Pattern : fourmi
Context :
Matches : {Fourmi}
IgnoreCase : True
LineNumber : 1
Line : Les fourmis sont très utiles.
Filename : fourmisUtiles.txt
Path : C:\Temp\FourmisUtiles.txt
Pattern : fourmi
Context :
Matches : {Fourmi}
Ainsi nous pouvons demander le numéro de ligne de la première occurrence :
PS > $Var[0].Linenumber
8
Exemple
Autre recherche simple.
Nous pouvons également utiliser Select-String en lui passant les données cibles au travers du pipeline comme cela :
Les résultats obtenus seront les mêmes que dans l’exemple précédent.
Ne vous trompez pas ! Utilisez bien Get-Item ou Get-ChildItem et non pas Get-Content car bien que
cela semble fonctionner, le résultat n’est généralement pas celui attendu. En effet, vous passeriez au pipeline le
contenu des fichiers et non pas les fichiers euxmêmes, et le contenu est en quelque sorte concaténé. Ce qui aurait pour
conséquence de fausser la valeur de la propriété LineNumber.
Exemple
IgnoreCase : True
LineNumber : 8
Line : Chez la fourmi sa voisine,
Filename : InputStream
Path : InputStream
Pattern : fourmi
Context :
Matches : {Fourmi}
IgnoreCase : True
LineNumber : 15
Line : La fourmi n’est pas prêteuse ;
Filename : InputStream
Path : InputStream
Pattern : fourmi
Context :
Matches : {Fourmi}
IgnoreCase : True
LineNumber : 23
Line : Les fourmis sont très utiles.
Filename : InputStream
Path : InputStream
Pattern : fourmi
Context :
Matches : {Fourmi}
Dans cet exemple, on notera que :
l Le nom de fichier a disparu des résultats pour être remplacé par un « InputStream » qui indique la provenance des
données.
l Tout lien avec le fichier d’origine ayant disparu, le numéro de ligne est relatif à l’ensemble du flux, ce qui donne un
résultat potentiellement erroné si l’on s’attend à avoir la position dans le fichier (voir la troisième et dernière
occurrence cidessus).
Exemple 3
Recherche à base d’expression régulière.
...
Cette ligne de commandes explore tous les fichiers dont l’extension est .txt à la recherche d’une chaîne se
terminant par World.
L’exemple précédent repose sur une expression régulière. Pour que Select-String fasse une recherche sur
une expression littérale plutôt que sur une expression régulière, il faut employer le paramètre -SimpleMatch.
Ce commutateur est nécessaire lorsque la chaîne de recherche contient des caractères spéciaux qui ont une signification
dans le langage des expressions régulières, tels que le dollar, le point, l’étoile, etc.
Exemple
Recherche dont le résultat est un booléen.
Exemple
Recherche d’une chaîne en affichant son contexte (2 lignes avant et 2 lignes après).
PS > Select-String Cigalefourmi.txt -Pattern ’Août’ -Context 2
Nous aurions pu ajouter le commutateur -SimpleMatch afin de préciser que notre recherche porte sur une chaîne
littérale et non pas sur une expression régulière. Cela fonctionne correctement car il n’y a pas de caractères
spéciaux dans notre chaîne de recherche.
7. Gestion des fichiers CSV
a. Import/export de données
Les fichiers CSV (CommaSeparated Values) sont des fichiers texte dont les valeurs sont séparées par un
séparateur. Généralement, la première ligne de ces fichiers est l’entête. Celuici comprend le nom de chaque
« colonne » de données ou « champ ». Le nom des champs ainsi que les valeurs sont séparés par un séparateur
qui est la virgule dans le milieu anglosaxon, et le pointvirgule dans le milieu francophone.
Voici un exemple de fichier CSV très simple :
Sexe;Prenom;Annee_de_naissance
F;Géraldine;1978
M;Stan;2008
F;Eléonore;2004
PowerShell comprend un jeu de deux commandes pour gérer ces fichiers : Export-CSV pour créer un fichier CSV,
Import-CSV pour lire un fichier CSV.
Voici les différents paramètres de Export-CSV :
Paramètre Description
-Encoding <String> Type d’encodage du fichier à créer (cf. Out-File pour la liste
des valeurs possibles). ASCII est le type par défaut avec
Windows PowerShell.
-NoTypeInformation <Switch> Par défaut, une ligne contenant le type des données
(commençant par #TYPE) est écrite avant l’entête. Si ce
commutateur est spécifié, cette ligne ne sera pas écrite.
-Delimiter <Char> Très utile, ce paramètre permet de spécifier un caractère
délimiteur pour séparer les valeurs de propriété. La valeur par
défaut est la virgule.
Et voici ceux de Import-CSV :
Paramètre Description
-Delimiter <Char> Très utile, ce paramètre permet de spécifier un caractère délimiteur pour
séparer les valeurs de propriété. La valeur par défaut est une virgule.
-UseCulture <Switch> En lieu et place du paramètre -Delimiter, vous pouvez également
utiliser -UseCulture. En spécifiant une culture spécifique, PowerShell
adaptera le délimiteur (le délimiteur pour la culture frFR est le point
virgule). Pour le vérifier, essayez : (Get-
Culture).TextInfo.ListSeparator.
-Header <String[]> Permet de spécifier une autre ligne d’entête que celle contenue dans le
fichier importé. Permet aussi de spécifier un entête aux fichiers CSV qui
n’en n’auraient pas.
Exemple : Export-CSV
#TYPE Selected.System.Diagnostics.EventLogEntry
"TimeGenerated","EntryType","Source","EventID"
"05/01/2015 20:46:26","Information","Windows Reporting","1001"
"05/01/2015 20:44:48","Information","ESENT","327"
"05/01/2015 20:44:48","Information","ESENT","326"
"05/01/2015 20:41:04","Information","Wlclntfy","6000"
"05/01/2015 20:41:02","Information","Wlclntfy","6003"
Faites attention, car par défaut Export-CSV génère des fichiers de type ASCII, autrement dit sans caractères
accentués. Il est donc important de ne pas oublier le type d’encodage souhaité lorsque l’on travaille en français.
Exemple 1 : Import-CSV
À présent, observons les propriétés et méthodes de l’objet contenu dans $journal :
TypeName: CSV:Selected.System.Diagnostics.EventLogEntry
Nous pouvons nous apercevoir que nous avons des propriétés qui correspondent au nom de notre ligne d’en
tête ; ce qui va être fort utile pour en récupérer les valeurs ! Par exemple :
PS > $journal[0].EventID
1001
Exemple 2 : Export-CSV et Import-CSV
Imaginons à présent que nous soyons confrontés à la recherche de résultats dans un fichier CSV puis à leurs
modifications. Prenons comme exemple le fichier suivant :
Nom;Prenom;Domaine;Derniere_Connexion
Lemesle;Robin;powershell-scripting.com;20/03/2018
Petitjean;Arnaud;powershell-scripting.com;19/09/2017
Teixeira;Jessica;;02/02/2018
Dans ce fichier, trois personnes sont identifiées. Parmi elles, l’une n’est pas identifiée comme appartenant au
domaine powershellscripting.com, et nous nous devons de corriger cette anomalie.
Dans un premier temps, importons le fichier.
PS > $utilisateurs = Import-Csv ./utilisateurs.csv -UseCulture
PS > $utilisateurs
PS > $utilisateurs[0]
PS > $utilisateurs[0].Nom
Lemesle
PS > $utilisateurs[0].Domaine
powershell-scripting.com
Bien entendu, le tableau peut être parcouru avec une boucle et chacune des valeurs est modifiable ; c’est le cas ci
dessous. Pour chaque utilisateur, si un domaine n’est pas spécifié alors on lui ajoute la valeur PowerShell-
scripting.com.
PS > $utilisateurs
Enfin, pour prendre en compte les changements apportés, l’enregistrement de la variable $utilisateurs dans
le fichier utilisateurs.csv s’effectue avec la commande Export-Csv.
L’avantage des fichiers CSV délimités avec le pointvirgule, c’est qu’ils sont reconnus nativement
dans Microsoft Excel. Ainsi lorsque vous double cliquerez sur le fichier CSV, Excel devrait
automatiquement le convertir et l’afficher.
b. Conversion de données au format CSV
Il est possible grâce à ConvertTo-CSV de transformer un tableau d’objets en un tableau de chaînes de
caractères au format CSV. ConvertTo-CSV remplit grosso modo la même fonction que Export-CSV (d’ailleurs il
accepte pratiquement les mêmes paramètres) sauf que la conversion est réalisée en mémoire et non vers un
fichier texte.
Cela apporte de la flexibilité dans le cas où nous souhaitons transformer les données avant de les écrire sur
disque.
Exemple 1 : Conversion d’un objet unique
"Nom";"Email";"siteWeb";"codePostal"
"Arnaud Petitjean";"arnaud@psh.local";"www.psh.local";"33950"
Exemple 2 : Conversion d’une collection d’objets
#TYPE Selected.System.Management.Automation.PSCustomObject
"Verb","Group"
"Add","Common"
"Clear","Common"
"Close","Common"
"Copy","Common"
"Enter","Common"
La commandelette Get-Verb retourne la liste des verbes approuvés par Microsoft pour ce qui
concerne le nommage des fonctions et en particulier des fonctions avancées lorsqu’elles sont
incluses dans un module. Si une fonction exportée par un module possède un verbe non approuvé,
alors un message d’avertissement sera affiché au chargement du module.
c. Conversion de données à partir du format CSV
Précédemment nous avons vu comment convertir un objet en un tableau de chaînes de caractères répondant au
format CSV, à présent nous allons faire l’opération inverse. À savoir, convertir des données répondant au format
CSV en objets.
Bien que la démarche puisse paraître curieuse au premier abord, vous allez vite comprendre, au travers des
exemples à suivre, les cas d’usages intéressants de la commandelette ConvertFrom-CSV.
Tout d’abord il faut savoir que ConvertFrom-CSV fonctionne parfaitement de concert avec sa sœ ur jumelle
ConvertTo-CSV ; cependant nous n’avons pas encore trouvé d’intérêt à convertir un objet natif en CSV puis à
faire l’opération inverse. Par contre, là où ConvertFrom-CSV prend tout son sens, c’est lorsque nous travaillons
avec des commandes héritées du système d’exploitation qui génèrent en sortie des données au format CSV.
Prenons par exemple le cas d’espèce de la commande Whoami.exe. Celleci retourne de nombreuses
informations sur l’utilisateur connecté : ses groupes d’appartenance, son SID, ainsi que de nombreuses autres
informations.
Essayons la commande suivante qui renvoie le SID de l’utilisateur connecté :
USER INFORMATION
----------------
User Name SID
=================== ===========================================
adps1\administrator S-1-5-21-3471033758-2576861177-32356578-500
Le résultat de cette commande est très intéressant, mais comment récupérer la valeur du SID afin de l’exploiter
dans un script ? La première idée qui vient en tête est celle qui consisterait à analyser le flux texte pour en
extraire le SID. Pourquoi pas ? Ceci dit, cette démarche est parfois risquée car si un caractère ou une ligne diffère
par rapport au résultat attendu, alors nous risquons de ne pas extraire la bonne valeur. Par conséquent l’idée est
ici d’exploiter la possibilité de cet utilitaire à générer un résultat au format CSV. Ainsi en ajoutant l’option /FO CSV
à la ligne de commande, nous lui demandons de fournir un flux texte au format CSV.
Essayons à nouveau en ajoutant /FO CSV :
"User Name","SID"
"adps1\administrator","S-1-5-21-3471033758-2576861177-32356578-500"
C’est mieux. Cela ressemble bien à un format CSV. Voyons maintenant le résultat si nous passons le retour de
cette ligne de commandes à ConvertFrom-CSV :
Ceci n’est certainement pas la meilleure approche pour récupérer le SID de l’utilisateur courant. Cet exemple a
uniquement pour but d’illustrer le fait qu’il est facile de convertir un flux texte au format CSV venant d’utilitaires
tierces et de les convertir en objets PowerShell.
Toujours avec l’utilitaire Whoami.exe il est possible de connaître les groupes d’appartenance de l’utilisateur
courant. Voyons comment convertir le résultat de façon à en récupérer un objet PowerShell.
En résumé, bien que d’apparence anodine, la commande ConvertFrom-CSV est très puissante et peut vous
rendre bien des services. N’hésitez donc pas à en abuser lorsque vous en avez la possibilité car ce sera bien plus
simple que de « parser » les résultats à l’aide d’expressions régulières complexes.
8. Gestion des fichiers XML
XML (Extensible Markup Language) est un langage basé sur une hiérarchisation des données sous forme de balise.
Pour savoir à quoi ressemble du XML, le mieux est certainement d’en voir un exemple.
<Livre>
<Titre>Windows PowerShell</Titre>
<SousTitre>Les fondamentaux</SousTitre>
<Auteur>
<Nom>Arnaud Petitjean</Nom>
<AnnéeDeNaissance>1974</AnnéeDeNaissance>
<Distinction>MVP PowerShell</Distinction>
</Auteur>
<Auteur>
<Nom>Robin Lemesle
</Nom>
<AnnéeDeNaissance>1985</AnnéeDeNaissance>
<Distinction>MVP PowerShell</Distinction>
</Auteur>
<Chapitre>
<Nom>Introduction</Nom>
<NombrePage>100</NombrePage>
</Chapitre>
<Chapitre>
<Nom>A la découverte de PowerShell</Nom>
<NombrePage>120</NombrePage>
</Chapitre>
</Livre>
Pour connaître l’ensemble des commandes liées à l’utilisation de fichier XML, tapez la commande suivante :
Commande Description
ConvertTo-Xml Crée une représentation XML (en mémoire) à partir d’objets .NET.
Export-Clixml Exporte un ou plusieurs objet(s) en une représentation XML dans un fichier. Ce
mécanisme est appelé « sérialisation d’objets ».
Import-Clixml Importe un fichier XML exclusivement généré par Export-CliXML et
convertit le résultat en représentation objet. Ce mécanisme est appelé
« désérialisation d’objets ».
Select-Xml Permet d’effectuer des requêtes de recherche au format XPath au sein d’un
document XML.
Import-Clixml permet uniquement l’importation de fichiers XML générés par la commande Export-
Clixml.
a. Chargement d’un fichier XML
Pour importer notre fichier d’exemple, il suffit de réaliser un transtypage sur un tableau de chaînes de caractères
retourné par Get-Content. Le fichier XML importé dans l’exemple suivant est celui présenté dans le fichier
Livre.xml.
Livre
-----
Livre
b. Gestion du contenu
Maintenant que le fichier XML est importé en mémoire dans la variable $Livre, il est alors très simple de le
parcourir. Chaque nœ ud qui le compose est à présent représenté sous forme de propriétés.
Exemples
PS > $Livre.Livre
PS > $Livre.Livre.Titre
Windows PowerShell
PS > $livre.Livre.auteur
Des modifications peuvent également être effectuées en mémoire, pour cela il suffit d’indiquer quelle nouvelle
valeur doit prendre un nœ ud en question.
Nous venons de voir comment explorer un fichier XML personnalisé, ce qui est très simple et pratique.
c. Export d’objets au format XML
PowerShell est doté de la commande ConvertTo-XML pour convertir un ou plusieurs objets en une
représentation XML. À noter que ConvertTo-XML ne crée pas de fichier, mais cette action est de notre
responsabilité.
Pour bien comprendre le fonctionnement de cette commande, prenons le petit exemple ciaprès où nous avons en
entrée un tableau d’objets personnalisé ; ici, des voitures. Ensuite, nous exporterons ce tableau en une
représentation XML que nous enverrons ensuite sur disque.
Définition du tableau d’objets :
$voitures = @(
[PSCustomObject]@{
Marque = ’AUDI’; Modele=’A4 Avant’; Puissance = 177
},
[PSCustomObject]@{
Marque = ’BMW’; Modele=’320i’; Puissance = 200
})
Résultat avant conversion :
PS > $voitures
Conversion du tableau en un flux de données texte au format XML :
Résultat après conversion :
PS > $XmlFile
Envoi du résultat dans un fichier texte :
d. Sérialisation/désérialisation avec les commandes *CliXML
Les commandes PowerShell pour la manipulation du contenu XML ont principalement pour vocation le stockage
d’informations, et plus particulièrement le stockage d’objets. Les objets émis par PowerShell sont en réalité ceux
du framework .NET. Ce mécanisme appelé sérialisation/désérialisation est bien connu de nos amis développeurs.
Pour mieux comprendre, prenons un exemple concret. Il y a des cas où nous souhaitons sauvegarder dans un
fichier l’état d’un objet et notamment toutes ses propriétés et valeurs associées, l’objectif étant de pouvoir recréer
fidèlement cet objet ultérieurement. C’est notamment ce qu’il se passe lorsque nous récupérons des informations
à partir d’une machine distante via les mécanismes de communication à distance PowerShell.
En effet, pour transiter via les protocoles HTTP/HTTPS, les données (qui sont en PowerShell toujours des objets)
ont besoin d’être converties en flux texte. Nous aurons l’occasion de reparler de ces mécanismes ultérieurement
dans cet ouvrage.
Pour le moment, contentonsnous d’exporter les processus en cours d’exécution sur notre machine. Nous allons en
quelque sorte prendre une photographie de l’état de notre système. Nous pourrons ainsi réimporter cet « état du
système » par la suite et le comparer avec les processus en cours d’exécution à un autre moment de la journée.
Cela permettrait par exemple d’identifier les processus manquants ou ceux identiques à différents instants.
Exemple
Sérialisation des processus en cours d’exécution.
Voyons à quoi ressemble notre fichier XML, du moins les premières lignes car il est très volumineux (de l’ordre de
plusieurs Mo) :
<Objs Version="1.1.0.1"
xmlns="http://schemas.microsoft.com/powershell/2004/04">
<Obj RefId="0">
<TN RefId="0">
<T>System.Diagnostics.Process</T>
<T>System.ComponentModel.Component</T>
<T>System.MarshalByRefObject</T>
<T>System.Object</T>
</TN>
<ToString>System.Diagnostics.Process (AppleOSSMgr)</ToString>
<Props>
<I32 N="BasePriority">8</I32>
<B N="HasExited">false</B>
<Obj N="Handle" RefId="1">
<TN RefId="1">
<T>System.IntPtr</T>
...
La grammaire (le schéma) utilisée ici pour la sérialisation d’objets est propre à PowerShell. Nous pouvons nous en
rendre compte grâce à la balise <Objs Version="1.1.0.1"
xmlns="http://schemas.microsoft.com/powershell/2004/04"> placée automatiquement au début
du fichier.
À présent, réimportons la photographie de l’état des processus prise précédemment et comparonsla avec les
processus actuellement en cours d’exécution pour voir la différence.
Exemple
Désérialisation des processus exportés précédemment.
PS > $Avant = Import-Clixml .\Processes.clixml
La variable $Avant contient maintenant les processus qui s’exécutaient au moment précis où nous avons pris la
photographie. En d’autres termes, elle contient le résultat de la commande Get-Process à la différence près
qu’un objet désérialisé perd toutes les méthodes de l’objet d’origine (mais pas les propriétés).
Nous pouvons maintenant comparer les noms des processus (ceux qui s’exécutaient précédemment avec ceux qui
s’exécutent en ce moment même) grâce à Compare-Object :
Name SideIndicator
---- -------------
chrome <=
PaintDotNet <=
Le résultat de la comparaison effectuée avec Compare-Object nous indique que les processus chrome et
PaintDotNet étaient en cours d’exécution précédemment.
9. Import/export de données au format JSON
Le format JSON est extrêmement populaire de nos jours. C’est le format le plus couramment retourné par les
services REST sur le Web. Il est facile de manipuler des fichiers JSON grâce au couple de commandes ConvertTo-
Json et ConvertFrom-Json.
a. Export de données
Prenons par exemple un tableau d’objets personnalisés que nous souhaitons convertir au format JSON. Reprenons
notre tableau de voitures, déjà utilisé précédemment avec ConvertTo-XML.
$voitures = @(
[PSCustomObject]@{
Marque = ’AUDI’; Modele=’A4 Avant’; Puissance = 177
},
[PSCustomObject]@{
Marque = ’BMW’; Modele=’320i’; Puissance = 200
})
Résultat avant conversion :
PS > $voitures
ConvertTo-Json possède le paramètre -Compress permettant de retourner une forme de résultat plus
compacte sans saut de ligne ni indentation.
Résultat converti en JSON compacté :
b. Import de données
Des données au format JSON peuvent être facilement converties en un tableau d’objets PowerShell grâce à la
commande ConvertFrom-Json.
Comme nous vous le disions en introduction, le format JSON est très utilisé comme format de renvoi de données
lorsque l’on consomme des services REST sur Internet.
Dans l’exemple ciaprès, nous allons récupérer le cours du BitCoin (en dollars US) auprès de la plateforme
d’Exchange Poloniex.
id : 121
last : 13447.20814455
lowestAsk : 13447.20814458
highestBid : 13447.20814455
percentChange : -0.05872196
baseVolume : 64652043.59451559
quoteVolume : 4780.90035758
isFrozen : 0
high24hr : 14449.02739320
low24hr : 12853.99999974
La propriété contenant le cours est visiblement la propriété last. Nous pouvons donc demander à PowerShell
qu’il nous retourne uniquement celleci.
PS > $res.USDT_BTC.last
13447.20814455
La valeur d’un BitCoin à l’heure où nous écrivons ces lignes est donc de 13447 $.
Ce qui impressionne le plus ici, ce n’est pas la valeur du BitCoin (quoique...), mais surtout le fait que
ConvertFrom-Json a converti « automagiquement » les données au format JSON renvoyées par Invoke-
WebRequest en une table de hachage qui contient un tableau de valeurs.
10. Export de données en tant que page HTML
Si la création de pages HTML vous tente pour présenter quelques rapports stratégiques ou autres, alors la
commandelette ConvertTo-HTML est faite pour vous ! En effet, grâce à celleci la génération de pages Web
devient presque un jeu d’enfant si l’on fait abstraction de la mise en forme.
Voici les paramètres disponibles de ConvertTo-HTML :
Paramètre Description
Un peu à la manière de la commande Export-CSV, le nom des propriétés servira de titre pour chaque colonne du
fichier HTML.
Exemple
Liste des services du système.
Cet exemple nous permet de créer une page HTML qui contient la liste des services, leurs noms ainsi que leurs
états. Le paramètre -Title a été spécifié afin que la fenêtre ait un titre autre que celui par défaut. Le tout est
passé à Out-File qui créera le fichier Services.htm.
Si nous ne spécifions pas de propriétés particulières, toutes celles de l’objet seront écrites ; le paramètre -
Property joue donc en quelque sorte un rôle de filtre.
D’autre part, à la différence d’Export-CSV, ConvertTo-HTML a besoin pour fonctionner pleinement qu’on lui
adjoigne une commandelette pour écrire le flux texte de génération de la page dans un fichier. Par conséquent,
n’oubliez pas de faire attention au type d’encodage du fichier résultant.
Pour ouvrir ce fichier directement dans Internet Explorer, il suffit de taper la commande suivante :
./services.htm ou Invoke-Item ./services.htm.
Comme l’extension .htm est connue de Windows, celuici ouvre le fichier avec l’application qui lui est associée
(par défaut, Internet Explorer).
Grâce au paramètre -Body, nous pouvons spécifier du contenu supplémentaire qui apparaîtra dans le corps de la
page, juste avant les données de l’objet.
Exemple
Liste des services du système avec BODY.
Liste des services du système formaté avec CSS.
Encore plus fort, nous allons cette fois encadrer notre tableau grâce aux feuilles de style en cascade (Cascading Style
Sheets). Pour ce faire, nous avons créé la feuille de style suivante, que nous avons nommée Style.css :
<style type=’text/css’>
table {
border: medium solid #000000;
border-collapse: collapse ;
}
td, th {
border: thin solid #6495ed;
}
</style>
Nous allons devoir inclure ce fichier dans l’élément HEAD de notre fichier HTML, comme ceci :
Exemple
Liste des services du système avec analyse du contenu.
PS > Get-Service |
ConvertTo-Html -Property name,displayname,status `
-Title ’Services du système’ -Body `
’<CENTER><H2>Etat des services du système</H2></CENTER>’ |
foreach {
if($_ -match ’<td>Running</td>’)
{
$_ -replace ’<tr>’, ’<tr bgcolor=#DDDDDD>’
}
elseif($_ -match ’<td>Stopped</td>’)
{
$_ -replace ’<tr>’, ’<tr bgcolor= #6699FF>’
}
else
{
$_
}
} | Out-File ./Services.htm
Dans cet exemple, lorsqu’un service est en marche, on remplace la balise TR (indiquant une ligne) par la même
balise TR avec en plus l’instruction permettant d’afficher un fond de couleur bgcolor=#codeCouleur.
11. Export de données avec OutGridView
Disponible uniquement sous Windows PowerShell, la commande Out-GridView permet d’afficher des données de
façon graphique (à l’image de ce que nous venons de faire précédemment avec une page HTML, mais sans effort) et
de manière interactive. L’interactivité de la table se trouve dans la possibilité de cliquer sur le titre des colonnes afin
d’effectuer un tri des données par ordre alphabétique. Une autre forme d’interactivité est une fonction de recherche
intégrée à la table. Celleci est pratique lorsque les données sont nombreuses et que l’on en recherche une en
particulier. Sachez qu’il est possible d’utiliser Out-GridView au milieu d’une ligne de commandes, ainsi les objets
sélectionnés seront transmis au pipeline.
Exemple 1
Liste des services en cours d’exécution.
Out-GridView peut éventuellement se substituer à un filtre Where-Object simplifié.
Exemple 2
Filtrage des services puis export CliXML.
Une variable contenant une date est de type DateTime. Pour le vérifier par vousmême, tapez la commande
suivante :
PS > (Get-Date).GetType()
Les objets de type DateTime possèdent de nombreuses propriétés et méthodes intéressantes comme, entre
autres, la méthode IsDaylightSavingTime, qui indique si l’heure actuelle est ajustée pour l’heure d’été ou
l’heure d’hiver.
Pour se rendre compte des possibilités d’actions sur les dates, le mieux est encore de faire appel à Get-Member sur
l’objet retourné par Get-Date :
Lorsque l’on parle de date ou de temps, il est nécessaire de définir ce que l’on appelle une unité de temps. Et l’unité
la plus basse qui soit dans le système est appelée un « tick ». Un tick est une unité de mesure du temps. Elle
correspond à un battement de cœ ur de l’ordinateur, c’estàdire à une période du Timer (le Timer est le composant
électronique qui gère le temps). Cette valeur vaut actuellement dix millionièmes de secondes.
Pour connaître le nombre de ticks présents en une seconde, tapez la commande suivante : $((Get-
Date).ticks) - $((Get-Date).Addseconds(-1).ticks)
Soit à peu près 10 millions moyennant le temps de traitement de la commande.
1. Manipulation des objets DateTime
La commandelette Get-Member appliquée à un objet de type DateTime retourne une liste considérable de
méthodes et de propriétés. Le tableau suivant reprend les méthodes les plus couramment utilisées et en donne une
brève description.
Méthode Description
Add Ajoute ou retranche une ou plusieurs unités de temps à l’objet
AddDays date. Ajoute si la valeur passée en argument est positive,
AddHours retranche si la valeur passée est négative.
AddMilliseconds
AddMinutes
AddMonths
AddSeconds
AddTicks
AddYears
CompareTo Compare une date à une autre. Les valeurs retournées sont :
-1 : si la date est antérieure à celle à laquelle on la compare.
1 : si elle est postérieure.
0 : si elles sont égales.
Equals Retourne un indicateur booléen de comparaison.
True : si les deux dates sont identiques.
False : dans le cas contraire.
GetDateTimeFormats Retourne tous les formats disponibles pour l’objet DateTime.
Subtract Soustrait une date de l’objet.
ToFileTime Retourne la valeur de l’objet DateTime en cours, en heure de
fichier Windows.
ToFileTimeUtc Retourne la valeur de l’objet DateTime en cours, en heure de
fichier Windows (Heure Universelle).
ToLocalTime Retourne la valeur de l’objet DateTime en cours, en heure
locale.
ToLongDateString Retourne une chaîne de caractères contenant la date au format
long.
ToLongTimeString Retourne une chaîne de caractères contenant l’heure au format
long.
ToOADate Retourne la date au format OLE (Object Linking and Embedding)
automation (nombre flottant). Le format OLE automation
correspond au nombre de jours depuis le 30 décembre 1899 à
minuit.
ToShortDateString Retourne une chaîne de caractères contenant la date au format
court.
ToShortTimeString Retourne une chaîne de caractères contenant l’heure au format
court.
ToString Retourne une chaîne de caractères contenant la date et l’heure au
format standard.
ToUniversalTime Retourne la date et l’heure au format standard.
Voici comment obtenir la liste des propriétés disponible sur un objet datetime :
DisplayHint : DateTime
DateTime : lundi 5 janvier 2015 23:07:44
Date : 05/01/2015 00:00:00
Day : 5
DayOfWeek : Monday
DayOfYear : 5
Hour : 23
Kind : Local
Millisecond : 926
Minute : 7
Month : 1
Second : 44
Ticks : 635560960649267742
TimeOfDay : 23:07:44.9267742
Year : 2015
Exemple
Récupération des minutes dans l’heure actuelle.
PS > (Get-Date).Minute
8
2. Formatage des dates
Dès lors que l’on parle de formats ou de formatage, il faut savoir que l’on ne va pas non plus récupérer un objet de
type datetime, mais une chaîne (type string). En effet, un format est une représentation textuelle d’un objet.
Le choix d’un format n’est pas forcément chose aisée, surtout quand il existe une soixantaine dits « standards ».
Eh oui, il existe de très nombreuses façons de représenter une date. Pour s’en rendre compte, essayons la
commande suivante :
05,01,15
05.01.15 23 h 09
05.01.15 23.09
05.01.15 23:09
05.01.15 23:09:30
05.01.15 23h09
05/01/15
05/01/15 23 h 09
05/01/15 23.09
05/01/15 23:09
05/01/15 23:09:30
05/01/15 23h09
05/01/2015
05/01/2015 23 h 09
05/01/2015 23.09
05/01/2015 23:09
05/01/2015 23:09:30
05/01/2015 23h09
05-01-15
05-01-15 23 h 09
05-01-15 23.09
05-01-15 23:09
05-01-15 23:09:30
05-01-15 23h09
2015-01-05
2015-01-05 23 h 09
2015-01-05 23.09
2015-01-05 23:09
2015-01-05 23:09:30
2015-01-05 23:09:30Z
2015-01-05 23h09
2015-01-05T23:09:30
2015-01-05T23:09:30.7970279+01:00
23 h 09
23.09
23:09
23:09:30
23h09
5 janv. 15
5 janv. 15 22:09:30
5 janv. 15 23 h 09
5 janv. 15 23.09
5 janv. 15 23:09
5 janv. 15 23:09:30
5 janv. 15 23h09
5 janvier
5 janvier 2015
5 janvier 2015 22:09:30
5 janvier 2015 23 h 09
5 janvier 2015 23.09
5 janvier 2015 23:09
5 janvier 2015 23:09:30
5 janvier 2015 23h09
janvier 2015
lundi 5 janvier 2015
lundi 5 janvier 2015 22:09:30
lundi 5 janvier 2015 23 h 09
lundi 5 janvier 2015 23.09
lundi 5 janvier 2015 23:09
lundi 5 janvier 2015 23:09:30
lundi 5 janvier 2015 23h09
Mon, 05 Jan 2015 23:09:30 GMT
a. Formats standards
Pour vous aider dans le choix du format, le tableau suivant dresse la liste les formats standards applicables aux
objets DateTime.
Format Description
d Format date courte.
D Format date longue.
f Format date longue et heure abrégée.
F Format date longue et heure complète.
g Format date courte et heure abrégée.
G Format date courte et heure complète.
m,M Format mois et jour : "dd MMMM".
r,R Format date et heure basé sur la spécification de la RFC 1123.
s Format date et heure triée.
t Format heure abrégée.
T Format heure complète.
u Format date et heure universelle (indicateur de temps universel : "Z").
U Format date longue et heure complète avec temps universel.
y,Y Format année et mois.
Voici quelques exemples d’applications des différents formats.
Exemples
Date au format standard tel que défini dans la RFC 1123.
Date au format date courte et heure complète telles que définies dans la RFC 1123.
Date au format date longue et heure complète tel que défini dans la RFC 1123.
b. Formats personnalisés
Bien entendu l’affichage d’une date ne se limite pas aux formats standards. Des affichages personnalisés sont
également possibles et ils constituent la technique la plus simple et rapide pour créer le format souhaité.
Et pour ce faire, il faut associer le paramètre -Format aux spécificateurs de format. La différence avec les formats
standard énoncés précédemment réside dans le fait que les formats personnalisés sont des éléments combinables
dans une chaîne de caractères par exemple, alors que les formats standards n’ont de sens que s’ils ne sont pas
combinés.
Voici la liste non exhaustive des formats personnalisés :
Format Description
d Représentation du jour par un nombre compris entre : 131.
dd Représentation du jour par un nombre compris entre : 0131. La différence avec le format « d »
est l’insertion d’un zéro non significatif pour les nombres allant de 1 à 9.
ddd Représentation du jour sous la forme de son nom abrégé. Exemple : Lun., Mar., Mer., etc.
dddd Représentation du jour sous la forme de son nom complet.
f Représentation du chiffre le plus significatif de la fraction de seconde.
ff Représentation des deux chiffres les plus significatifs de la fraction de seconde.
fff Représentation des trois chiffres les plus significatifs de la fraction de seconde.
ffff Représentation des quatre chiffres les plus significatifs de la fraction de seconde.
h Représentation de l’heure par un nombre. Nombres compris entre : 112.
hh Représentation de l’heure par un nombre avec insertion d’un zéro non significatif pour les
nombres allant de 1 à 9. Nombres compris entre : 0112.
H Représentation de l’heure par un nombre. Nombres compris entre : 023.
HH Représentation de l’heure par un nombre avec insertion d’un zéro non significatif pour les
nombres allant de 0 à 9. Nombres compris entre : 0023.
m Représentation des minutes par un nombre. Nombres compris entre : 059.
mm Représentation des minutes par un nombre avec insertion d’un zéro non significatif pour les
nombres allant de 0 à 9. Nombres compris entre : 0059.
M Représentation du mois par un nombre. Nombres compris entre : 112.
MM Représentation du mois par un nombre avec insertion d’un zéro non significatif pour les nombres
allant de 1 à 9. Nombres compris entre : 0112.
MMM Représentation du mois sous la forme de son nom abrégé.
MMMM Représentation du mois sous la forme de son nom complet.
y Représentation de l’année sous la forme d’un nombre à deux chiffres, au plus. Si l’année
comporte plus de deux chiffres, seuls les deux chiffres de poids faible apparaissent dans le
résultat et si elle en comporte moins, seuls le ou les chiffres (sans zéro significatif) apparaissent.
yy Idem que cidessus à la différence près que si l’année comporte moins de deux chiffres, le
nombre est rempli à l’aide de zéros non significatifs pour atteindre deux chiffres.
yyy Représentation de l’année sous la forme d’un nombre à trois chiffres. Si l’année comporte plus
de trois chiffres, seuls les trois chiffres de poids faible apparaissent dans le résultat. Si l’année
comporte moins de trois chiffres, le nombre est rempli à l’aide de zéros non significatifs pour
atteindre trois chiffres.
yyyy Représentation de l’année sous la forme d’un nombre à quatre chiffres. Si l’année comporte plus
de quatre chiffres, seuls les quatre chiffres de poids faible apparaissent dans le résultat. Si
l’année comporte moins de quatre chiffres, le nombre est rempli à l’aide de zéros non significatifs
pour atteindre quatre chiffres.
Pour obtenir la liste complète de ces spécificateurs de format, rendezvous sur le site MSDN de Microsoft :
http://msdn2.microsoft.com/frfr/library/8kb3ddd4(VS.80).aspx
Exemple
Dans ce premier exemple, nous souhaitons simplement afficher la date sous le format suivant : <Nom du
Jour><Numero du jour> <Mois> <Année> ---- <Heure>:<Minute>:<Seconde>
Exemple
Imaginons que vous soyez amené à générer des rapports dont le nom du fichier doit correspondre à la date à laquelle il a été
généré.
Pour cela, rien de plus facile...
Résultat
Il existe un dernier mode d’affichage. Ce dernier s’appelle l’affichage en mode Unix.
Comme vous pouvez l’imaginer, ce mode utilise les spécificateurs de format Unix. Voici l’essentiel des
spécificateurs :
Format Description
%m Mois de l’année (0112).
%d Jour du mois (0131).
%y Année, uniquement les deux derniers chiffres (0099).
%Y Année sur quatre chiffres.
%D Affichage au format mm/dd/yy.
%H Heures (0023).
%M Minutes (0059).
%S Secondes (0059).
%T Heure au format HH:MM:SS.
%J Jour de l’année (1366).
%w Jour de la semaine (06) avec Samedi = 0.
%a Abréviation du jour (lun. , mar. , etc.).
%h Abréviation du mois (Fev., Juil. , etc.).
%r Heure au format HH:MM:SS avec HH (012).
%n Nouvelle ligne.
%t Tabulation.
Exemple
Affichage au format Unix de la date actuelle.
3. Manipulation des dates
a. Créer une date
Il existe plusieurs manières de créer une date en PowerShell. La plus courante consiste à utiliser la commande
Get-Date. Utilisée sans paramètre, cette commande retourne un objet représentant la date et l’heure courante.
Si nous désirons créer un objet DateTime contenant la date de notre choix, nous pouvons le spécifier grâce aux
paramètres : -Year, -Month, -Day, -Hour, -Minute, -Second.
Exemple
Si nous souhaitons définir une variable qui contient la date du 1er février 2015, la commande sera la suivante :
Notez que tout paramètre qui n’est pas précisé prend la valeur correspondante de la date actuelle.
b. Modifier une date
Lors de l’exécution d’un script ou pour une application tierce nous pouvons être amenés à modifier une date
donnée. Pour répondre à cela, il faut utiliser la famille des méthodes Add*.
Les entiers relatifs sont l’ensemble des entiers (0,1,2,3...) positifs et négatifs (0,1,2,3...).
Par exemple, pour savoir quel jour de la semaine sera le même jour qu’aujourd’hui mais dans un an, il suffit
d’ajouter un an à la date du moment et de récupérer le jour de la semaine correspondant.
Monday
De la même façon, il est facile de retrouver le jour de sa naissance.
Exemple
Lorsque l’on spécifie une date comme dans l’exemple cidessus, le format attendu est le format anglosaxon, à
savoir : mois/jour/année.
c. Comparer des dates
Il existe plusieurs types de comparaison de dates, la comparaison la plus simple s’effectue avec la méthode
CompareTo. Appliquée à la variable de type DateTime, cette méthode permet une comparaison rapide et
renvoie les valeurs suivantes :
Valeur de retour Description
-1 Si la date est antérieure à celle à laquelle on la compare.
1 Si elle est postérieure.
0 Si elles sont égales.
Exemple
Comparaison de la date de deux fichiers.
Pour cela, il suffit de récupérer une à une les dates de création et de les comparer avec la méthode CompareTo.
d. Calculer un intervalle entre deux dates
Il est très facile de calculer le temps écoulé entre deux dates. Cette opération est rendue possible grâce à la
commandelette New-TimeSpan. Le résultat de cette commande émet non pas un objet DateTime mais un objet
TimeSpan.
Exemple
Calcul du temps écoulé depuis votre naissance.
Pour déterminer le nombre de secondes qui se sont écoulées depuis votre naissance, il faut, dans un premier
temps, calculer le temps écoulé grâce à la commande New-TimeSpan. Puis dans un second temps, récupérer la
propriété TotalSeconds, comme cidessous.
Construisons d’abord la variable $DateNaiss qui recevra notre date de naissance :
Nous pouvons également la construire ainsi, c’est au choix :
PS > $DateNaiss = [datetime]’1985/10/06 08:30’
Puis nous faisons appel à New-TimeSpan et lui passons la variable contenant la date de naissance ainsi que la
date et heure courante :
Days : 10683
Hours : 14
Minutes : 54
Seconds : 10
Milliseconds : 333
Ticks : 9230648503339945
TotalDays : 10683,6209529397
TotalHours : 256406,902870554
TotalMinutes : 15384414,1722332
TotalSeconds : 923064850,333994
TotalMilliseconds : 923064850333,994
À présent, il n’y a plus qu’à sélectionner la propriété TotalSeconds ainsi :
923064911,014729
La commande New-TimeSpan retourne un objet de type TimeSpan. Pour observer toutes les méthodes
applicables au type TimeSpan, tapez la commande suivante : New-Timespan | Get-Member.
e. Conversion d’une date exprimée en ticks
Il arrive relativement fréquemment que nous recevions des valeurs entières longues pour exprimer des dates
comme par exemple une valeur du genre « 130088918630153620 ». Une telle valeur correspond au nombre de
ticks écoulés depuis le 1e r janvier de l’an 1601 à 0h00.
C’est le cas par exemple des dates retournées par l’API ADSI lorsque l’on manipule Active Directory Domain
Services mais c’est aussi le cas de certaines classes .NET.
Dans un cas de figure de cette espèce, pas de panique ! Bien que PowerShell ne dispose pas de commandes pour
la conversion d’une telle date, nous allons quand même pouvoir nous en sortir en attaquant directement le
framework .NET.
En effet, la classe DateTime permet nativement de prendre en charge un tel format, ainsi nous pouvons écrire
ceci :
PS > [DateTime]130088918630153620
Afin « d’augmenter » une date, il suffit de lui ajouter des années grâce à la méthode AddYears(). Ainsi, pour
ajuster le résultat nous devons lui ajouter 1600 années :
PS > ([DateTime]130088918630153620).AddYears(1600)
À présent, il nous faut ajuster la date UTC afin de la convertir en date locale, c’estàdire dans un format tenant
compte du fuseau horaire sur lequel nous sommes.
Pour récupérer le fuseau horaire, nous allons utiliser la classe TimeZoneInfo et en particulier sa propriété
statique Local, tel qu’indiqué cidessous :
PS > [TimeZoneInfo]::Local
La dernière étape consiste à modifier notre date en lui appliquant le fuseau horaire. Pour ce faire, nous utiliserons
la méthode statique ConvertTimeFromUtc(), toujours de la classe TimeZoneInfo :
Pour clôturer cette partie, nous allons transformer ce petit bout de script bien utile en une fonction car nous nous
en resservirons plus loin dans cet ouvrage.
C’est ainsi que nous pouvons écrire la fonction suivante :
function ConvertTo-LocalTime
{
[CmdletBinding()]
Param (
[Parameter(Mandatory=$true)]
[Int64]$LongDate
)
$myUTCTime = ([DateTime]$LongDate).AddYears(1600)
[TimeZoneInfo]::ConvertTimeFromUtc($myUTCTime, [TimeZoneInfo]::Local)
}
À présent, testons notre fonction :