Portée des variables PowerShell : Compréhension de la portée des variables

Quand vous écrivez un script PowerShell sans fonctions et sans dépendances externes vis-à-vis d’autres scripts, le concept des étendues PowerShell n’est pas vraiment une préoccupation majeure. Les variables globales PowerShell ne sont pas au premier plan. Cependant, lorsque vous commencez à construire des fonctions, des modules et à apprendre à 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 étendues en PowerShell, comment elles fonctionnent et comment vous pouvez écrire du code en tenant compte des étendues. À la fin, vous comprendrez les variables globales de PowerShell et bien plus encore !

Étendues : Un peu comme des seaux

Avez-vous déjà écrit un script où vous définissez une variable et lorsque vous vérifiez la valeur de cette variable, elle est différente ? Vous pouvez vous demander comment cette variable a changé alors que vous l’avez clairement définie. Une raison peut être que la valeur de la variable est écrasée dans une autre étendue.

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 est probable que ces variables soient dans un autre « seau » qui n’est pas disponible à ce moment-là.

Les étendues sont comme des seaux. Elles affectent la manière dont PowerShell isole les variables, les alias, les fonctions et les PSDrives entre différentes zones. Une étendue est comme un seau. C’est un endroit pour rassembler tous ces éléments ensemble.

Quand PowerShell démarre, il crée automatiquement ces « seaux » pour vous. À ce stade, vous utilisez déjà des étendues sans vous en rendre compte. Toutes les étendues sont définies par PowerShell et sont créées sans aucune intervention de votre part.

Types de portée

Lorsque PowerShell démarre, il crée automatiquement quatre « compartiments » ou portées pour divers éléments à placer. Vous ne pouvez pas créer de portées par vous-même. Vous ne pouvez qu’ajouter et supprimer des éléments de ces portées définies ci-dessous.

Portée globale

Les éléments définis lorsque PowerShell s’ouvre sont définis à la portée globale. Ces éléments comprennent des objets créés par le système comme les lecteurs PowerShell et tout ce que vous avez défini dans un profil PowerShell puisque votre profil s’exécute au démarrage.

Les éléments dans la portée globale sont disponibles partout. Vous pouvez référencer des éléments dans la portée globale de manière interactive sur la console, dans n’importe quel script que vous exécutez, et dans n’importe quelle fonction. Les variables globales de PowerShell sont omniprésentes. 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ègne en général.

Portée du script

A script scope is automatically created every time a PowerShell script runs. You can have many different script scope instances. Script scopes are created when you execute a PS1 script or a module, for example.

Seuls les éléments créés dans cette instance de portée de script particulière peuvent se référencer mutuellement.

Portée privée

Typiquement, un élément défini peut être accédé à partir d’autres portées – ce n’est pas vrai avec les éléments dans une portée privée. Les éléments dans une portée privée contiennent des objets qui sont cachés aux autres portées. Une portée privée est utilisée pour créer des éléments portant le même nom que des éléments dans d’autres portées pour éviter les chevauchements.

Portée locale

Contrairement aux autres étendues, la portée locale est un peu différente. La portée locale est un pointeur vers la portée globale, de script ou privée. La portée locale est relative au 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 aborderons plus tard), il ira dans la portée locale.

Référencement des portées

Maintenant que vous avez une idée des quatre types de portées qui existent, sachez qu’il existe deux façons de référencer ces portées.

En PowerShell, il existe deux façons de référencer les portées – par nom ou par numéro. Les deux méthodes référencent les mêmes portées, mais simplement de manière différente. Ce sont deux façons différentes d’interagir avec les portées.

Portées nommées

Auparavant, dans la section Type de portée, vous avez appris que les portées étaient référencées par nom. Référencer une portée par son nom est, de manière intuitive, appelé une portée nommée. Le but principal de référencer une portée par son nom est d’assigner un élément à une portée. Vous apprendrez comment faire cela ci-dessous.

Portées numérotées

En plus d’un nom, chaque portée a un numéro commençant à zéro qui sera toujours la portée locale. Les portées sont numérotées dynamiquement par rapport à la portée locale actuelle.

Par exemple, dès que vous ouvrez une session PowerShell, vous opérez dans la portée globale. À ce stade, la portée globale est la portée locale (rappelez-vous que la portée locale n’est qu’un pointeur).

Depuis que la portée locale est toujours la portée zéro, à ce stade, la portée globale est également 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 portée locale a alors changé pour la portée du script. Maintenant, la portée du script est la portée zéro et la portée globale est la portée un.

Les portées sont numérotées Ce processus se répète pour autant de portées que vous avez où la portée locale est 0. Les portées sont numérotées dynamiquement par la hiérarchie des portées.

Hiérarchie des portées et héritage

Comme mentionné précédemment, lorsque vous lancez une session PowerShell, PowerShell crée certains éléments pour vous dans la portée globale. 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.

Étant donné que vous êtes dans la portée globale par défaut, si vous faites quelque chose qui crée une autre portée comme l’exécution d’un script ou l’exécution d’une fonction, une portée enfant sera créée avec le parent étant la portée globale. Les portées sont comme des processus avec des parents et des enfants.

Tout ce qui est défini dans une portée parente, la portée globale, dans ce cas, sera accessible dans la portée enfant. 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 est assigné une valeur dans la portée locale (script tandis que le script s’exécute). Lorsque Test.ps1 est exécuté, vous pouvez voir ci-dessous que vous ne pouvez pas y faire référence après l’exécution du script. Puisque $a a été assigné dans la portée du script, la portée globale (dans la console interactive) ne peut pas le voir.

Global scope cannot see the variable

Prenons cet exemple un peu plus loin et faisons en sorte que le script Test.ps1 ressemble à ce qui suit. Le script tente maintenant de produire la valeur de $a avant de la définir dans la même portée.

Write-Output $a
$a = 'Hello world!'

Pour démontrer, attribuez une valeur à $a dans la console interactive. Cela attribue la valeur à la portée globale. Maintenant, lorsque le script s’exécute, il héritera de la portée parente (globale) et devrait pouvoir 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 dans 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 (globale).

Variable is available in script and global scopes

Souvenez-vous de ce comportement d’héritage de portée. En le faisant, cela vous aidera lorsque des conflits de variables surviendront, comme des variables dans différentes portées avec le même nom.

Définition et Accès aux Éléments dans les Portées

Maintenant que vous savez ce qu’est une portée et comment elles fonctionnent, comment y accédez-vous ? Voyons comment utiliser PowerShell pour définir une portée de variable (et y accéder).

Obtenir/Définir-Variable

En 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.

Les deux cmdlets sont similaires avec des paramètres Nom et Portée. En utilisant ces paramètres, PowerShell vous permet de définir et de récupérer des valeurs de variable 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 local 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 Portée 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 a

Name         Value
----         -----
a            foo

Portées privées/Script/Globales

Vous utiliserez les mêmes paramètres (Nom et Valeur) lorsque vous travaillerez avec des variables privées, de script et globales également. La seule différence cette fois-ci est que vous utiliserez le paramètre Portée pour définir explicitement la portée.

Les méthodes pour définir une variable à portée privée, de script ou globale sont les mêmes. Remplacez simplement la valeur passée au paramètre Portée par Privée, Script, Globale.

PS> Set-Variable -Name a -Value 'foo' -Scope <Private|Script|Global>

Pour récupérer la valeur d’un script ou d’une variable à portée globale, 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 faire référence aux portées en utilisant des numéros de portée au lieu de noms avec les cmdlets Get/Set-Variable.

Préfixage de la portée

Vous pouvez également récupérer et définir des variables dans des portées en utilisant un raccourci. Au lieu d’utiliser les cmdlets PowerShell, vous préfixerez la portée à la variable lors de sa référence.

Portées locales

Étant donné que la portée locale est toujours la valeur par 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 portée locale.

PS> $a = 'foo'
PS> $a

foo

Portées privées/du script/globales

Si vous souhaitez définir et référencer des portées de script ou globales, vous pouvez préfixer les variables avec le nom de la portée suivi d’un point-virgule.

Par exemple, pour définir la variable $a dans la portée globale, vous pouvez la préfixer avec $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érencez de la même manière. Notez également que vous pouvez exclure le préfixe de portée si la portée définie est la portée locale.

PS> $global:a = 'foo'
PS> $global:a
foo
PS> $a
foo

Portées dans les blocs de script

PowerShell dispose d’une construction pratique appelée blocs de script. Les blocs de script vous permettent de déplacer des fragments de code et de les exécuter presque n’importe où.

Tout comme un script PS1, les blocs de script s’exécutent dans leur propre portée de script. Lorsque vous exécutez un bloc de script, vous exécutez essentiellement un script PS1.

Remarquez dans l’exemple ci-dessous où une variable est définie dans la portée globale, puis une tentative de la réécrire dans une portée de script. Comme vous l’avez appris précédemment, cela ne fonctionnera pas car une portée enfant ne peut pas faire référence à une portée parente.

A child scope cannot overwrite a parent scope

Cet exemple montre que lorsque $a est modifié dans le bloc de script, la définition de variable globale pour $a n’est pas modifiée car le bloc de script est une portée enfant.

Scripts d’Inclusion (Échange de Portées Locales)

PowerShell a un concept appelé inclusion par point. C’est une méthode qui vous permet d’exécuter un script PS1 et de ramener tout ce qui serait de portée de script dans la portée locale à la place.

En mettant un point (.) avant de référencer un script PS1 et en l’exécutant, cela « inclus par point » le contenu du script et ramène tout dans la portée locale.

Pour illustrer, j’ai de 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 incluez ce script comme indiqué ci-dessous. Remarquez que la variable d’origine a été écrasée. PowerShell a « fusionné » les deux portées locales ensemble.

Original variable overwritten

Utilisation de la propriété AllScope (Option)

Vous avez vu comment interagir avec des éléments dans des portées spécifiques, mais jusqu’à présent, chaque élément est toujours défini dans une seule portée. Mais que faire si vous ne savez pas dans quelle portée une variable est définie ?

Lors de la définition d’une variable avec la cmdlet Set-Variable, vous pouvez placer la variable dans toutes les portées en une seule fois. Pour ce faire, utilisez la valeur AllScope pour le paramètre Option.

Pour illustrer, le script Test.ps1 a été modifié pour définir une variable dans toutes les portées. Cette variable est ensuite affichée comme indiqué ci-dessous.

Set-Variable -Name a -Value 'Hello world!' -Option AllScope
Write-Output $a

Vous pouvez ensuite 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 ne pas avoir d’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.

Variable overwritten in global scope

L’option AllScope est pratique, mais soyez prudent. Cette option élimine essentiellement le concept de portées et fusionne tout ensemble.

Portées de fonction

Lorsque vous exécutez une fonction, tout le code à l’intérieur de cette fonction est dans sa propre portée enfant. Les portées de fonction suivent le même comportement enfant/parent que les autres portées.

Avoir des portées distinctes pour chaque fonction est une bonne idée. Cela permet un meilleur contrôle sans avoir à se soucier des conflits d’éléments. Cela offre également l’avantage supplémentaire de la suppression automatique des variables dans une fonction. Dès que la fonction est terminée, tous les éléments définis dans la fonction seront effacés.

Pour le démontrer, 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-Thing
PS> $var

Garder les éléments privés (Désactiver l’héritage)

En général, si une variable est définie dans une portée parent, cette variable sera également définie dans la portée enfant. Mais peut-être souhaitez-vous utiliser un nom de variable déjà défini dans l’une des portées à exécuter dans une session. Dans ce cas, vous pouvez soit choisir un nom de variable différent, soit définir la variable dans une portée privée pour en faire une variable privée.

A way to use scopes to reduce conflicts is to use the private scope. Using the private scope disables inheritance on that specific variable. When multiple child scopes are created, those child scopes will not see any variables defined in a private scope.

A Test.ps1 script outputs the value of $a as shown below.

Write-Output $a

Vous pouvez voir ci-dessous que je définis une variable à portée privée dans la portée globale, puis j’exécute le script Test.ps1. Normalement, lorsque vous définissez une variable dans une portée parent, cette variable est 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 en exécutant le script Test.ps1 ne pouvait pas voir la variable $a à portée privée définie dans la portée parent.

Script scope cannot see the private-scoped variable

Contrairement aux portées globales ou à l’option AllScope de la commande Set-Variable, les variables à portée privée sont un excellent moyen de compartimenter les éléments.

Meilleures pratiques de portée

Penser que définir des variables dans la portée globale ou utiliser l’option AllScope est la meilleure approche est courant. Après tout, toutes les variables sont disponibles partout. Pas besoin de se préoccuper des complications liées aux portées. Bien que cela offre une liberté supplémentaire pour accéder à ce qui est défini, cela peut rapidement devenir incontrôlable et difficile à déboguer.

Au lieu d’essayer d’éviter d’utiliser des portées, suivez ces conseils :

  1. Au lieu de spécifier des portées dans les fonctions, utilisez des paramètres pour transmettre les informations nécessaires à la fonction.
  2. Restez autant que possible dans la portée locale.
  3. Au lieu de définir des variables globales à partir d’un script, utilisez la cmdlet Write-Output pour afficher tout et enregistrez-le dans une variable lorsque nécessaire depuis la console.

Le principal point ici est d’adopter les portées et d’apprendre à les utiliser à votre avantage au lieu d’essayer de les contourner.

Lecture complémentaire

Source:
https://adamtheautomator.com/powershell-scopes/