PARTIE 1 | PARTIE 2 | PARTIE 3 | PARTIE 4 | ||
---|---|---|---|---|---|
Sommaire général | Exécuter Powershell | Les cmdlets | Instructions dans des scripts | Aide, gestion d’erreurs et manipulation de fichiers | Références |
Cet article indique quelques instructions utilisables dans un script Powershell. La liste d’instructions n’est pas exhaustive toutefois elle devrait permettre d’écrire rapidement un script avec les principales instructions.
Manipulation de variables
Effectuer des “casts”
if…then…else
Operateurs de comparaison
Opérateurs logiques
switch
Utilisation de “wildcards”
Définition des listes d’éléments
Arrays
Listes .NET
Table de hashage
Boucles
Boucle “while”
do…while
do…until
Boucle “for”
Boucle “foreach”
“break” et “continue”
Utilisation de “labels” avec “break” et “continue”
Scripts blocks
Creation d’un “script block” à partir d’une chaîne de caractères
“return”
Passage de paramètres à un “script block”
Méthode 1: collection d’arguments
Méthode 2: utiliser “param”
Vérifier qu’un argument est manquant
Valeur par défaut
“script block” et “pipelines”
Mot clé “param” avec les “script blocks”
Portée des variables dans un “script block”
Portée des paramètres dans un “script block”
Utiliser “$global” pour modifier une variable de façon globale
Utiliser “$private” pour modifier une variable de façon locale
Fonctions
Passer des variables par référence
Les “pipelines” dans une fonction
Filtres
Utiliser des “switch”
Quelques instructions courantes utilisables dans les scripts
Ces instructions peuvent être utiles pour implémenter des scripts.
Manipulation de variables
L’affectation de variable se fait en utilisant le caractère $
, par exemple:
$var1 = "value 1"
$var2 = $var1
L’affectation de la variable nulle se fait en écrivant:
$var1 = $null
Effectuer des “casts”
On peut effectuer des casts en indiquant le type souhaité avec des crochets [ ]
. L’indication du type peut se faire de 2 façons:
# Effectuer un "cast" vers DateTime
[System.DateTime]$var1="2017-08-25"
# Effectuer un "cast" vers un XmlDocument
$var2 = [System.Xml.XmlDocument]"<xml><node>HERE</node></xml>"
Dans certains cas, powershell effectue implicitement des casts, par exemple si on écrit:
$stringVar = "PowerShell"
$doubleVar = 2.0
$result = $stringVar + $doubleVar
Dans ce cas le cast de $doubleVar
en string se fera implicitement.
En revanche, si on écrit:
$result = $doubleVar + $stringVar # Cette ligne occasionne une erreur
Il y aura une erreur car Powershell ne pourra pas convertir $doubleVar
en string.
Une solution peut consister à utiliser l’opérateur -as
:
$result = ($doubleVar -as [string]) + $stringVar
On peut aussi effectuer le cast explicitement:
$result = [string]$doubleVar + $stringVar
if…then…else
Pour implémenter une clause if...then...else
, la syntaxe est semblable au C#:
Par exemple:
$var = 2
if ($var -eq 1)
{
# ...
}
else
{
# ...
}
Il n’y a pas de elseif
, il faut utiliser des if ... then ... else
imbriqués.
Operateurs de comparaison
D’autres opérateurs de comparaison sont disponibles:
-eq
: comparateur d’égalité-ne
: “not equal to”, la valeur estTrue
si les opérandes ne sont pas égales.-ge
: “greater than or equal”, la valeur estTrue
si l’opérande de gauche est supérieure ou égale à l’opérande de droite.-gt
: “greater than”, la valeur estTrue
si l’opérande de gauche est strictement supérieure à l’opérande de droite.-le
: “less than or equal”, la valeur estTrue
si l’opérande de gauche est inférieure ou égale à l’opérande de droite.-lt
: “less than”, la valeur estTrue
si l’opérande de gauche est strictement inférieure à l’opérande de droite.-like
et-notlike
: permet d’effectuer des comparaisons de chaines de caractères en utilisant des wildcards:?
pour désigner un seul caractère non spécifié*
pour désigner un ou plusieurs caractères non spécifiés
Par exemple:
$var -like "*uary"
-match
et-notmatch
: permet de vérifier si une chaine de caractères respecte une expression régulière, par exemple:$string -match "\w"
(se reporter à Regular Expression Language – Quick Reference pour plus de précisions sur les expressions régulières).-contains
et-notcontains
: permet de tester si une valeur se trouve dans une liste. Par exemple:$names = "val1", "val2", "val3" $names -Contains "val2"
-is
et-isnot
: permet de tester le type d’une variable .NET (même opérateur qu’en C#). Par exemple:$stringToTest = "chaine de caracteres" ($stringToTest -is [System.String])
Opérateurs logiques
Ces opérateurs permettent de tester plusieurs expressions logiques:
-and
: opérateur ET-or
: opérateur OU-xor
: opérateur OU exclusif (retourneTrue
si seulement une expression est vraie)-not
: opérateur NOT
Par exemple:
$var1="value1"
$var2="value2"
($var1 -eq "value1") -and ($var2 -eq "value2")
switch
Pour implémenter un switch...case
, on utilise la syntaxe suivante:
switch ($var)
{
21 { "value1"; break }
22 { "value2" } # pas d'arrêt dans ce cas car pas de break
23 { "value3"; break }
default { "default" }
}
On peut executer une liste de type:
switch (1, 2, 3, 0)
Le parcours se fait dans l’ordre des valeurs.
Par défaut la comparaison n’est pas sensible à la casse:
Par exemple:
switch ("AAA")
{
"aAA" { "OK" }
"aaA" { "OK" }
"aaa" { "OK" }
}
Pour que la comparaison soit sensible à la casse, il faut ajouter -casesensitive
, par exemple:
switch -casesensitive ("AAA")
{
# ...
}
Utilisation de “wildcards”
On peut utiliser des wildcards, par exemple:
switch -wildcard ("AAA")
{
"AA*" {"OK"}
"AA?" {"OK"}
"A??" {"OK"}
}
Définition des listes d’éléments
Arrays
On peut définir des tableaux simples de la façon suivante:
$tab1=@(1, 2, 3, 4, 5, 6)
$tab2=1, 2, 3, 4, 5, 6
L’accès aux éléments du tableau se fait classiquement avec des crochets:
$tab1[3]
# On peut utiliser une variable contenant la valeur d'un index:
$i=2
$tab2[$i]
Listes .NET
On peut utiliser aussi des listes .NET de cette façon:
$dotnetList=New-Object Collections.Generic.List[string]
# Pour ajouter un élément:
$dotnetList.Add("first item")
# Pour accéder à un élément à un index particulier:
$dotnetList.Item(0)
Table de hashage
Une table de hashage peut se définir de cette façon:
$hashTable= @{
"key1" = "value1";
"key2" = "value2";
"key3" = "value3";
"key4" = "value4"
}
L’accès à un élément se fait en écrivant: $hashTable["key3"]
.
Boucles
Boucle “while”
Une boucle while
s’implémente de cette façon:
$i = 0
while ($i -le 5)
{
$i = $i + 1
}
do…while
L’implémentation du do...while
se fait de cette façon:
$i = 0
do
{ $i = $i + 1 } # un équivalent à cette incrémentation est $i++
while ($i -le 5)
do…until
Pour coder une boucle do...until
, on peut écrire:
$i = 0
do
{ $i++ }
until ($i -gt 5)
Boucle “for”
La boucle for
s’implémente de façon classique:
for ($f = 0; $f -le 5; $f++)
{
# ...
}
Boucle “foreach”
De même la boucle foreach
est proche de son équivalent en C#:
$array = 1, 2, 3, 4
foreach ($item in $array)
{
"`$item = $item"
}
On peut utiliser une boucle foreach
avec le résultat de cmdlets, par exemple:
Set-Location "<chemin sur le disque>"
foreach ($file in Get-ChildItem)
{
$file.Name
}
“break” et “continue”
On peut utiliser break
comme en C# pour stopper l’exécution d’une boucle:
foreach ($file in Get-ChildItem)
{
break
}
De même qu’en C#, on peut aussi utiliser “continue” pour passer à l’itération suivante sans exécuter les instructions se trouvant après le continue
.
Utilisation de “labels” avec “break” et “continue”
Les labels sont des espèces de “goto” pour indiquer à quel niveau d’une boucle, on souhaite que l’exécution se poursuive. Ces labels permettent d’effectuer des sauts vers une boucle particulière quand on se trouve dans des foreach
imbriqués:
Dans l’exemple suivant, le label est outsideloop
et break outsideloop
permet de stopper de la première boucle foreach
:
:outsideloop foreach( ... )
{
foreach ( ...)
{
break outsideloop
}
}
L’exécution va donc s’arrêter au niveau de la 1ere boucle.
On peut aussi utiliser des “labels” avec continue
:
:outsideloop foreach( ... )
{
foreach ( ...)
{
continue outsideloop
}
}
Dans ce cas, l’exécution va continuer au niveau de la première boucle foreach
.
Scripts blocks
Un script block est un bloc de code qu’on peut définir au préalable et qui se sera pas exécuté au moment de cette définition. On pourra exécuter ce bloc de code par la suite quand on le désire. L’intérêt des script blocks est de pouvoir définir et exécuter une “mini-fonction” dans le corps même d’une fonction.
Les blocs sont désignés avec des accolades { }
.
Pour définir un script block, dans un premier temps on l’affecte à une variable:
$var = { Clear-Host; "Powershell" }
Ensuite dans un 2e temps, pour l’exécuter on peut écrire:
&$var
# ou directement
& { .... }
Pour utiliser le résultat de l’exécution d’un script block:
$value = (41 + 1)
1 + (& $value)
Remarque: il faut rejouter des parenthèses et écrire (& $value)
pour que le corps du script block soit interprêté.
Dans le cas d’un script block, certains éléments sont consommés et d’autres ne le sont pas.
Par exemple, on écrit:
$value = { 42; Write-Host "Powershell" }
42
n’est pas consommé et il peut être utilisé comme valeur dans une instruction suivante.
En revanche Write-Host "Powershell"
est consommé (car affiché au moment de l’exécution).
Si on exécute:
1 + (& $value)
On obtient 43
.
De même si on exécute:
$value2 = & $value
La variable $value2
contient 42
.
On peut exécuter un script block avec l’instruction Invoke()
:
$var = { Clear-Host; "Powershell" }
$var.Invoke()
On peut aussi utiliser la cmdlet Invoke-Command
:
$var = { Clear-Host; "Powershell" }
Invoke-Command -ScriptBlock $var
Creation d’un “script block” à partir d’une chaîne de caractères
On peut créer un script block à partir d’une chaîne de caractères en écrivant:
$newScript = [scriptblock]::Create("Get-ChildItem")
# ou
$newScript = [System.Management.Automation.ScriptBlock]::Create("Get-ChildItem")
“return”
En utilisant le mot clé return
, on peut stopper l’exécution:
$value = { return 42; Write-Host ... }
Ainsi Write-Host
ne sera pas exécuté.
Passage de paramètres à un “script block”
Pour passer des paramètres à un script block, il existe 2 méthodes:
Méthode 1: collection d’arguments
Les arguments sont passés dans une collection, dans l’exemple la collection est $arg
:
$qa = { $question = $args[0]
$answer = $args[1]
}
Si on exécute l’instruction:
& $qa "value1" "value2"
alors arg[0]
est égal à value1
et arg[1]
est égal à value2
.
Avec l’instruction Invoke()
avec des arguments, la syntaxe est:
$qa.Invoke("value1", "value2")
Avec Invoke-Command
:
Invoke-Command -ScriptBlock $qa -ArgumentList 'value1','value2'
Méthode 2: utiliser “param”
En utilisant le mot clé param
on peut définir une liste d’arguments, par exemple:
$qa = { param( $question, $answer )
Write-Host $question
Write-Host $answer
}
A l’exécution, pour passer les paramètres la syntaxe est similaire à la méthode de la collection d’arguments:
& $qa "value1" "value2"
On peut aussi utiliser la syntaxe suivante:
& $qa -question "value1" -answer "value2"
On peut aussi utiliser seulement les initiales des paramètres:
& $qa -q "value1" -a "value2"
La syntaxe avec Invoke-Command
est similaire à la méthode précédente:
Invoke-Command -ScriptBlock $qa -ArgumentList 'value1','value2'
Vérifier qu’un argument est manquant
Pour vérifier si un argument est manquant, on peut écrire:
$qa = { param($question, $answer )
if (!$answer) # si la variable n'est pas affectée
{
# ...
}
}
Valeur par défaut
Pour affecter une valeur par défaut à un argument, on peut écrire:
$qa = { param($question, $answer = "default value" )
# ...
}
Forcer le type des paramètres:
$point = { param([int] $x, [int] $y)
# ...
}
“script block” et “pipelines”
Il est possible d’utiliser des script blocks avec des pipelines. Toutefois les pipelines se définissent dans ce cas en utilisant des mot clés process
, begin
et end
qui seront utilisés pour désigner des blocs de code à exécuter:
process
: bloc de code qui constitue le corps du script block dans le pipeline.begin
: bloc de code qui sera exécuté avant d’exécuter le corps du script block dans le pipeline (i.e. ce bloc sera exécuté avant celui deprocess
).end
: bloc de code qui sera exécuté après d’exécuter le corps du script block dans le pipeline (i.e. ce bloc sera exécuté après celui deprocess
).
Par exemple:
$pipeline = {
process {
if ($_.Name -like "*.ps1" )
{
return $_.Name
}
}
}
Le mot clé process
est utilisé pour indiquer les instructions qui seront exécutées à l’exécution du script block dans le pipeline.
Par exemple pour exécuter le script block définit dans la variable $pipeline
dans un pipeline, on peut écrire:
Get-ChildItem | &$pipeline
En utilisant begin
et end
pour définir des instructions à exécuter avant et après l’exécution le script block dans un pipeline:
$pipeline = {
begin { $val = "value1" }
process {
if ($_.Name -like "*.ps1" )
{
return $_.Name
}
}
end { return $val }
}
Mot clé “param” avec les “script blocks”
On peut aussi utliser le mot clé param
pour un script bloc utilisé dans un pipeline:
$pipeline = {
param ( $headerText )
begin { ... }
process { ... }
end { ... }
}
Par exemple si on exécute le code suivant:
$pipeline = {
param ( $headerText )
begin { Write-Host "Executé avant process: $headerText" }
process { Write-Host "Execution de process: $headerText" }
end { Write-Host "Executé après process: $headerText" }
}
& $pipeline "value1"
Le résultat sera:
Executé avant process: value1
Execution de process: value1
Executé après process: value1
Portée des variables dans un “script block”
Le terme employé dans la documentation pour qualifier la portée des variables est “scope”.
Quelques règles s’appliquent aux variables:
- Les variables déclarées à l’extérieur du script block sont utilisables dans le script block.
- Les variables modifiées dans le script block ont une portée limitée au script block. La valeur ne sera pas modifiée à l’extérieur du script block.
Portée des paramètres dans un “script block”
Le paramètre -scope
utilisé dans un script block avec les cmdlets Get-Variable
et Set-Variable
permettent d’indiquer des perimètres de portée. Ainsi suivant le périmètre qu’on a définit, le comportement différe du comportement par défaut.
Par exemple, en utilisant -scope 1
on indique que l’on souhaite avoir la valeur du parent du script block:
$var = 42
& { $var = 33;
Write-Host "$var"
Write-Host "Parent: " (Get-Variable var -valueOnly -scope 1 )
}
Dans ce cas la valeur de "$var"
sera 33
alors que la valeur de "Parent: " (Get-Variable var -valueOnly -scope 1 )
sera Parent: 42
De même avec Set-Variable
, -scope 1
permet d’indiquer qu’on souhaite modifier la valeur de la variable au niveau du parent:
$var = 42
Write-Host "Valeur au niveau parent: $var"
& { Set-Variable var 33 -scope 1
Write-Host "Valeur dans le script block: $var"
}
Write-Host "Valeur après le script block: $var"
Le résultat de cette exécution est:
Valeur au niveau parent: 42
Valeur dans le script block: 33
Valeur après le script block: 33
Utiliser -scope 1
n’est pas trés clair, il est préférable d’utiliser global
ou private
.
Utiliser “$global” pour modifier une variable de façon globale
Le mot clé $global
permet de modifier la valeur d’une variable de façon globale. En reprenant l’exemple précédent:
$var = 42
Write-Host "Valeur au niveau parent: $var"
& { $global:var = 33
write-host "Valeur dans le script block: $var" }
Write-Host "Valeur après le script block: $var"
Le résultat de cette exécution est:
Valeur au niveau parent: 42
Valeur dans le script block: 33
Valeur après le script block: 33
$global:
permet d’indiquer qu’on souhaite modifier la valeur du parent. Il s’utilise avec les deux points :
et sans le $
.
Utiliser “$private” pour modifier une variable de façon locale
Avec le mot clé $private
la valeur d’une variable ne sera pas affectée dans le script block, par exemple:
$value = 42
Write-Host "Valeur au niveau parent: $value"
& { $private:value = 21
Write-Host "Valeur dans le script block: $value" }
Write-Host "Valeur après le script block: $value"
Le résultat est:
Valeur au niveau parent: 42
Valeur dans le script block: 21
Valeur après le script block: 42
Dans ce cas la valeur de $value
n’est pas modifiée que dans le script block.
Fonctions
Les fonctions sont comme les script blocks sauf qu’elles sont définies de façon plus globale et non à l’intérieur d’une autre fonction.
Pour définir une fonction:
function NomFonction ( $value1, $value2)
{
# ...
Write-Host $value1
Write-Host $value2
}
Pour appeler la fonction:
NomFonction "val1" "val2"
# ou
$par1 = "val1"
$par2 = "val2"
NomFonction $par1 $par2
On n’utilise pas de parenthèses pour indiquer les paramètres lors de l’appel, on indique directement les paramètres séparés d’un espace.
Passer des variables par référence
Pour qu’une variable soit passé en argument par référence, on utilise le mot clé [ref]
. L’intérêt de passer une variable par référence est de pouvoir modifier sa valeur dans le corps de la fonction et de l’utiliser à l’extérieur de celle-ci.
Par exemple dans le code suivant, la valeur de la variable $par1
sera 33 à l’extérieur du corps de la fonction après exécution de cette fonction:
function NomFonction ([ref] $par1)
{
$par1.Value = 33
Write-Host "Valeur modifiée par la fonction: "$par1.Value
}
Si on exécute la fonction précédente de cette façon:
$value = 23
Write-Host "Valeur avant la fonction: $value"
NomFonction([ref] $value)
Write-Host "Valeur après la fonction: $value"
Le résultat est:
Valeur avant la fonction: 23
Valeur modifiée par la fonction: 33
Valeur après la fonction: 33
Il faut ajouter .value
car avec [ref]
powershell considère que la variable est de type object
.
Pour appeler une fonction avec [ref]
, on écrit:
NomFonction([ref] $var)
Ainsi si on utilise [ref]
et si la fonction change la valeur de la variable, la valeur sera modifiée à l’extérieur de la fonction.
Les “pipelines” dans une fonction
La syntaxe est la même que pour les script blocks. Il faut utiliser les mot clés process
, begin
et end
pour désigner des blocs de code à exécuter:
process
: bloc de code qui sera exécuté dans le pipeline.begin
: bloc de code qui sera exécuté avant le blocprocess
.end
: bloc de code qui sera exécuté après le blocprocess
.
Par exemple si on définit la fonction:
function NomFonction()
{
begin { Write-Host "Exécuté avant process" }
process { Write-Host "Exécution de process" }
end { Write-Host "Exécuté après process" }
}
Et si on exécute la fonction avec:
NomFonction
Le résultat est:
Exécuté avant process
Exécution de process
Exécuté après process
Filtres
Les filtres sont des espèces de fonctions sans paramètres. L’intérêt des filtres est de les utiliser dans des pipelines.
Pour définir un filtre, on utilise le mot clé filter
:
filter NomFiltre
{
# ...
Write-Host $_.Name
}
$_
permet d’utiliser la valeur d’un élément à une itération du pipeline.
On peut combiner des fonctions et des filtres dans un pipeline.
Par exemple, si on exécute la ligne suivante:
Get-ChildItem | NomFiltre
On obtient seulement le noms des fichiers se trouvant dans le répertoire courant.
Utiliser des “switch”
Les switch
permettent d’indiquer des paramètres facultatifs dans une fonction sous la forme de booléen. Quand ces paramètres ne sont pas renseignés, ils n’ont pas de valeur. Les switch
ne sont pas des paramètres facultatifs mais seulement des booléens qui peuvent être utilisés pour activer ou désactiver une fonctionnalité.
Par exemple, en utilisant le mot clé switch
devant les paramètres $verbose
et $debug
on peut indiquer que ces paramètres sont des booléens facultatifs:
function NomFonction()
{
param([switch] $verbose, [switch] $debug)
if ($verbose.IsPresent)
{
Write-Host "Verbose est présent"
}
else
{
Write-Host "Verbose n'est pas présent"
}
}
$verbose.IsPresent
permet de tester si la variable $verbose
a une valeur ou non.
Pour appeler une fonction qui utilise des switch
, on peut écrire:
NomFonction -verbose -debug
Dans ce cas là, le résultat est:
Verbose est présent
Si on exécute la ligne suivante:
NomFonction -debug
Le résultat sera:
Verbose n'est pas présent
L’article suivant permet de détailler la syntaxe pour afficher des messages d’aide, de gérer des erreurs dans les fonctions et comporte des exemples pour manipuler des fichiers.
Partie 3: instructions dans des scripts Powershell
Partie 4: aide, gestion d’erreurs et manipulation de fichiers