Lorsque vous écrivez un script PowerShell sans fonctions et sans dépendances externes sur d’autres scripts, le concept de portées PowerShell n’est pas très préoccupant. Le concept de variables globales PowerShell n’est pas au premier plan. Mais lorsque vous commencez à construire des fonctions, des modules et que vous apprenez à appeler des scripts à partir d’autres scripts, le sujet devient plus important.
Dans cet article, vous allez recevoir une leçon approfondie sur ce que sont les scopes dans PowerShell, comment ils fonctionnent, et comment vous pouvez écrire du code avec des scopes à l’esprit. Lorsque vous aurez terminé, vous comprendrez les variables globales de PowerShell et bien plus encore !
Table des matières
- Scopes : Un peu comme les seaux
- Types de scopes
- Scope global
- Portée de script
- Scope privé
- Portée locale
- Référencer des scopes
- Scopes nommés
- Scopes numérotés
- Hiérarchie et héritage des scopes
- Définir et accéder aux éléments dans les scopes
- Get/Set-Variable
- Portées locales
- Portées privées/script/globales
- Scope « Prefacing »
- Scopes locaux
- Scopes privés/Script/Global
- Les portées dans les blocs de script
- Dot Sourcing des scripts (permutation des scopes locaux)
- Utiliser la propriété AllScope (Option)
- Scopes de fonction
- Garder les éléments privés (désactiver l’héritage)
- Scoping Best Practices
- Lecture complémentaire
Scopes : Un peu comme les seaux
Vous avez déjà écrit un script où vous définissez une variable et quand vous vérifiez la valeur de cette variable, c’est autre chose ? Vous pouvez vous gratter la tête en vous demandant comment cette variable a changé alors que vous l’aviez clairement définie. Une raison peut être que la valeur de la variable se fait écraser dans une autre portée.
Peut-être vous êtes-vous demandé comment certaines variables PowerShell ont des valeurs lorsque vous les référencez dans votre console mais n’existent pas dans vos scripts. Il y a de fortes chances que ces variables se trouvent dans un autre » seau » qui n’est pas disponible à ce moment-là.
Les scopes sont comme des seaux. Les scopes affectent la façon dont PowerShell isole les variables, les alias, les fonctions et les PSDrives entre différentes zones. Un scope est comme un seau. C’est un endroit pour rassembler tous ces éléments.
Lorsque PowerShell démarre, il crée automatiquement ces « seaux » pour vous. À ce moment-là, vous utilisez déjà les scopes sans vous en rendre compte. Tous les scopes sont définis par PowerShell et sont créés sans aucune aide de votre part.
Types de scopes
Lorsque PowerShell démarre, il crée automatiquement quatre « seaux » ou scopes dans lesquels divers éléments peuvent être placés. Vous ne pouvez pas créer de scopes par vous-même. Vous pouvez uniquement ajouter et supprimer des éléments de ces scopes définis ci-dessous.
Scope global
Les éléments définis à l’ouverture de PowerShell sont placés au niveau du scope global. Ces éléments incluent les objets créés par le système comme les lecteurs PowerShell et aussi tout ce que vous avez défini dans un profil PowerShell puisque votre profil s’exécute au démarrage.
Les éléments de la portée globale sont disponibles partout. Vous pouvez référencer les éléments de la portée globale de manière interactive sur la console, dans tout script que vous exécutez et dans toute fonction. Les variables globales de PowerShell sont partout. Pour cette raison, l’utilisation courante des variables globales de PowerShell est d’utiliser les variables globales de PowerShell entre les scripts.
Il n’y a qu’une seule portée globale qui régit l’ensemble.
Portée de script
Une portée de script est automatiquement créée chaque fois qu’un script PowerShell s’exécute. Vous pouvez avoir de nombreuses instances de script scope différentes. Les scopes de script sont créés lorsque vous exécutez un script PS1 ou un module, par exemple.
Seuls les éléments créés dans cette instance particulière de script scope peuvent se référer les uns aux autres.
Scope privé
Typiquement, un élément défini est accessible depuis d’autres scopes – ce qui n’est pas vrai avec les éléments dans un scope privé. Les éléments dans un scope privé, contiennent des objets qui sont cachés aux autres scopes. Une portée privée est utilisée pour créer des éléments avec le même nom que les éléments dans d’autres portées pour éviter le chevauchement.
Portée locale
Contrairement aux autres portées, la portée locale est un peu différente. Le scope local est un pointeur vers le scope global, script ou privé. La portée locale est relative à n’importe quel contexte dans lequel le code s’exécute à ce moment-là.
Si vous créez une variable, un alias, une fonction ou un PSDrive sans lui attribuer explicitement une portée (que nous couvrirons plus tard), il ira dans la portée locale.
Référencer des scopes
Maintenant que vous avez une idée sur les quatre types de scopes qui existent, vous devez aussi savoir qu’il y a deux façons de référencer ces scopes.
Dans PowerShell, il y a deux façons de référencer des scopes – nommés ou numérotés. Les deux méthodes référencent les mêmes scopes mais simplement d’une manière différente. Ce sont deux façons différentes d’interagir avec les scopes.
Scopes nommés
Au-dessus, dans la section Type de scope, vous avez appris les scopes référencés par nom. Référencer une portée par son nom est, intuitivement, appelé une portée nommée. L’objectif principal de référencer une portée par son nom est d’affecter un élément à une portée. Vous apprendrez comment faire cela ci-dessous.
Scopes numérotés
En plus d’un nom, chaque scope a un numéro commençant à zéro qui sera toujours le scope local. Les scopes sont numérotés dynamiquement par rapport au scope local actuel.
Par exemple, dès que vous ouvrez une session PowerShell, vous opérez dans le scope global. À ce moment, la portée globale est la portée locale (rappelez-vous que la portée locale est juste un pointeur).
Puisque la portée locale est toujours la portée zéro, à ce moment, la portée globale est aussi actuellement la portée zéro. Mais, lorsque vous exécutez un script à partir de cette même session, une portée de script est créée. Lors de l’exécution, le pointeur de la portée locale a alors changé pour la portée du script. Maintenant, le scope de script est le scope zéro et le scope global est le scope un.
Les scopes sont numérotés Ce processus se répète pour autant de scopes que vous avez où le scope local est 0. Les scopes sont numérotés dynamiquement par la hiérarchie des scopes.
Hiérarchie et héritage des scopes
Comme mentionné précédemment, lorsque vous lancez une session PowerShell, PowerShell crée certains éléments pour vous dans le scope global. Ces éléments peuvent être des fonctions, des variables, des alias ou des PSDrives. Tout ce que vous définissez dans votre session PowerShell sera également défini dans la portée globale.
Puisque vous êtes dans la portée globale par défaut, si vous faites quelque chose qui crée une autre portée comme exécuter un script ou exécuter une fonction, une portée enfant sera créée avec le parent étant la portée globale. Les scopes sont comme des processus avec des parents et des enfants.
Tout ce qui est défini dans un scope parent, le global scope, dans ce cas, sera accessible dans le child scope. Mais ces éléments ne sont modifiables que dans la portée dans laquelle ils ont été définis.
Disons que vous avez un script appelé Test.ps1. À l’intérieur de ce script se trouve une seule ligne comme indiqué ci-dessous.
$a = 'Hello world!'
Lorsque vous exécutez ce script, $a
se voit attribuer une valeur dans la portée locale (script en cours d’exécution). Lorsque Test.ps1 est exécuté, vous pouvez voir ci-dessous que vous êtes incapable de le référencer après l’exécution du script. Puisque $a
a été assigné alors qu’il était dans la portée du script, la portée globale (à la console interactive) ne peut pas le voir.
Passons cet exemple un peu plus loin et faisons en sorte que le script Test.ps1 ressemble à ce qui suit. Le script tente maintenant de sortir la valeur de $a
avant de la définir dans la même portée.
Write-Output $a$a = 'Hello world!'
Pour démontrer, assignez une valeur à $a
à la console interactive. Cela affecte la valeur à la portée globale. Maintenant, le script s’exécute, il héritera de la portée parent (global) et devrait être en mesure de voir la valeur.
Vous pouvez voir ci-dessous que lorsque Test.ps1 est exécuté (créant une portée enfant de la portée globale), il peut voir la valeur de $a
. Vous pouvez également voir que la valeur de la variable est disponible à la portée globale également puisque cette portée est celle où elle a été définie. Cela signifie que $a
est disponible à la fois dans les portées du script (enfant) et du parent (global).
Souvenez-vous de ce comportement d’héritage de portée. En faisant cela vous aidera lors du dépannage des occasions les conflits de variables se produiront tels que les variables dans différents scopes avec le même nom.
Définir et accéder aux éléments dans les scopes
Maintenant que vous savez ce qu’est un scope et comment ils fonctionnent, comment y accéder ? Voyons comment utiliser PowerShell pour définir une portée de variable (et y accéder).
Get/Set-Variable
Dans PowerShell, il existe deux cmdlets qui vous permettent de définir des variables appelées Get-Variable
et Set-Variable
. Ces cmdlets vous permettent de récupérer la valeur d’une variable ou de définir une valeur.
Ces deux cmdlets sont similaires avec un paramètre Name
et Scope
. À l’aide de ces paramètres, PowerShell vous permet de définir et de récupérer des valeurs de variables dans toutes les portées.
Portées locales
Pour définir une variable dans une portée locale, utilisez Set-Variable
et fournissez-lui un nom de variable locale et une valeur comme indiqué ci-dessous.
PS> Set-Variable -Name a -Value 'foo'
La portée locale est toujours la valeur par défaut, donc ne pas utiliser le paramètre Scope
définira toujours la variable dans la portée locale.
Pour récupérer la valeur d’une variable à portée locale, utilisez Get-Variable
en lui fournissant le nom.
PS> Get-Variable -Name aName Value---- -----a foo
Portées privées/script/globales
Vous utiliserez les mêmes paramètres (Name
et Value
) lorsque vous travaillerez avec des variables privées, script et globales également. La seule différence est que cette fois, vous utiliserez le paramètre Scope pour définir explicitement la portée.
Les méthodes pour définir une variable privée, de script ou à portée globale sont les mêmes. Il suffit de remplacer la valeur passée au paramètre Scope
par Private
, Script
Global
.
PS> Set-Variable -Name a -Value 'foo' -Scope <Private|Script|Global>
Pour récupérer la valeur d’une variable script ou globally-scoped, utilisez Get-Variable
en lui fournissant le nom et la portée.
PS> Get-Variable -Name a -Scope <Script|Global>Name Value---- -----a foo
Note : Vous pouvez également référencer les scopes en utilisant des numéros de scope au lieu des noms avec les cmdlets
Get
/Set-Variable
.
Scope « Prefacing »
Vous pouvez également récupérer et définir des variables dans les scopes en utilisant un raccourci. Au lieu d’utiliser les cmdlets PowerShell, vous préfacerez le scope à la variable lorsque vous la référencerez.
Scopes locaux
Puisque le scope local est toujours le défaut, il suffit de définir une variable et de la référencer pour définir et récupérer une variable de scope local
PS> $a = 'foo'PS> $afoo
Scopes privés/Script/Global
Si vous souhaitez définir et référencer des scopes de script ou globaux, vous pouvez préfacer les variables avec le nom du scope et un point-virgule.
Par exemple, pour définir la variable $a
dans la portée globale, vous pouvez faire précéder a de $global:
.
$global:a = 'foo'
La même chose peut être faite pour une variable à portée de script.
$script:a = 'foo'
Une fois que les variables sont définies dans la portée préférée, vous les référenceriez alors de la même manière. Aussi, remarquez que vous pouvez exclure la préface de la portée si la portée définie est la portée locale.
PS> $global:a = 'foo'PS> $global:afooPS> $afoo
Les portées dans les blocs de script
PowerShell a une construction pratique appelée blocs de script. Les scriptblocks vous permettent de déplacer des extraits de code et de les exécuter à peu près n’importe où.
Comme un script PS1, les scriptblocks s’exécutent dans leur propre scope de script. Lorsque vous exécutez un scriptblock, vous exécutez essentiellement un script PS1.
Notez dans l’exemple ci-dessous où une variable est définie dans la portée globale et ensuite tentée d’être écrasée dans une portée de script. Comme vous l’avez appris ci-dessus, cela ne fonctionnera pas parce qu’une portée enfant ne peut pas faire référence à une portée parent.
Cet exemple montre que lorsque $a
est modifié dans le bloc de script, la définition de la variable globale pour $a
n’est pas modifiée parce que le bloc de script est une portée enfant de script.
Dot Sourcing des scripts (permutation des scopes locaux)
PowerShell possède un concept appelé dot-sourcing. Il s’agit d’une méthode qui vous permet d’exécuter un script PS1 et d’amener tout ce qui serait script-scopé dans la portée locale à la place.
En mettant un point (.) avant de référencer un script PS1 et de l’exécuter, cela « dot-source » le contenu du script et amène tout dans la portée locale.
Pour démontrer, j’ai à nouveau un script Test.ps1 qui définit une variable comme indiqué ci-dessous.
$a = 'Hello world!'
Dans une console PowerShell, définissez une valeur pour une variable $a
puis faites un dot source de ce script comme indiqué ci-dessous. Remarquez que la variable d’origine a été écrasée. PowerShell a « fusionné » les deux scopes locaux ensemble.
Utiliser la propriété AllScope (Option)
Vous avez vu comment interagir avec des éléments dans des scopes spécifiques, mais jusqu’à présent chaque élément est toujours défini dans un seul scope. Mais que faire si vous ne savez pas dans quelle portée une variable est définie ?
Lorsque vous définissez une variable avec le cmdlet Set-Variable
, vous pouvez placer la variable dans toutes les portées à la fois. Pour ce faire, utilisez la valeur AllScope
pour le paramètre Option
.
Pour démontrer, le script Test.ps1 a été modifié pour définir une variable a dans tous les scopes. Cette variable est ensuite sortie comme indiqué ci-dessous.
Set-Variable -Name a -Value 'Hello world!' -Option AllScopeWrite-Output $a
Vous pouvez alors voir ci-dessous qu’une valeur est définie pour $a dans la portée globale et que le script Test.ps1 est exécuté. Cependant, au lieu de n’avoir aucun effet, la valeur de $a
a été écrasée. Non seulement elle a été définie dans la portée du script (Write-Output $a
) mais elle a également écrasé la portée globale.
L’option AllScope
est pratique mais soyez prudent. Cette option supprime essentiellement le concept de scopes et fusionne tout ensemble.
Scopes de fonction
Lorsque vous exécutez une fonction, tout le code dans cette fonction est dans son propre scope enfant. Les scopes de fonction suivent le même comportement enfant/parent que les autres scopes.
Avoir des scopes séparés pour chaque fonction est une bonne idée. Il permet un meilleur contrôle sur les éléments n’ayant pas à se soucier des éléments en conflit, Il donne également l’avantage supplémentaire du nettoyage automatisé des variables dans une fonction. Dès que la fonction se termine, tous les éléments définis dans la fonction seront effacés.
Pour faire une démonstration, copiez/collez la fonction ci-dessous directement dans la console PowerShell.
Function Do-Thing { $var = 'bar'}
Une fois collée, exécutez la fonction. Remarquez que vous ne pouvez pas accéder à la variable $var
en dehors de la fonction.
PS> Do-ThingPS> $var
Garder les éléments privés (désactiver l’héritage)
Typiquement, si une variable est définie dans une portée parent, cette variable sera définie dans la portée enfant. Mais peut-être que vous aimeriez utiliser un nom de variable mais que ce nom de variable a déjà été défini dans l’un des scopes à exécuter dans une session. Dans ce cas, vous pouvez soit choisir un nom de variable différent, soit définir la variable dans un scope privé, ce qui en fait une variable privée.
Une façon d’utiliser les scopes pour réduire les conflits est d’utiliser le scope privé. L’utilisation de la portée privée désactive l’héritage sur cette variable spécifique. Lorsque plusieurs scopes enfants sont créés, ces scopes enfants ne verront aucune variable définie dans une portée privée.
Un script Test.ps1 sort la valeur de $a comme indiqué ci-dessous.
Write-Output $a
Vous pouvez voir ci-dessous que je définis une variable à portée privée à la portée globale et que j’exécute ensuite le script Test.ps1. Habituellement, lorsque vous définissez une variable dans une portée parent, cette variable sera disponible dans la portée enfant – ce n’est pas le cas avec une variable à portée privée.
Dans l’exemple ci-dessous, vous pouvez voir que la portée enfant du script créée par l’exécution du script Test.ps1 ne pouvait pas voir la variable $a
à portée privée définie dans la portée parent.
Contrairement aux portées globales ou à l’option AllScope
du cmdlet Set-Variable
, les variables à portée privée sont un excellent moyen de compartimenter les éléments.
Scoping Best Practices
Penser que définir des variables dans la portée globale ou utiliser l’option AllScope
est la voie à suivre est commun. Après tout, toutes les variables sont disponibles partout. Il n’y a pas besoin de se soucier des complications des scopes. Bien que cela fournisse une liberté supplémentaire pour l’accès à ce qui est défini, cela peut rapidement devenir incontrôlable et devenir difficile à dépanner.
Au lieu d’essayer d’empêcher l’utilisation des scopes, suivez ces conseils :
- Au lieu de spécifier les scopes dans les fonctions, utilisez les paramètres pour passer les informations requises à la fonction.
- Rester dans la portée locale autant que possible.
- Au lieu de définir des variables globales à partir d’un script, utilisez le cmdlet
Write-Output
pour tout sortir et l’enregistrer dans une variable au besoin depuis la console.
Le point principal ici est d’embrasser les scopes et d’apprendre à les utiliser à votre avantage au lieu d’essayer de les contourner.
Lecture complémentaire
- A propos des fonctions avancées et des paramètres de fonction
- Utiliser les variables d’environnement dans PowerShell
.