à propos des étendues dans Powershell
J'apprends à propos des portées dans Powershell et j'ai quelques questions:
- À propos de la "portée locale": D'après ce que j'ai lu, la portée locale est toujours la portée actuelle. Ainsi, par défaut, lorsque nous créons un élément (sans modificateur de portée), par exemple une variable, dans une certaine portée, que ce soit script ou global, la portée sera script/global en conséquence. Ma question est donc la suivante : quand devrons-nous spécifier explicitement le
localmodificateur ? - MSDN dit :
Vous pouvez créer une nouvelle étendue en exécutant un script ou une fonction, en créant une session ou en démarrant une nouvelle instance de PowerShell. Lorsque vous créez une nouvelle étendue, le résultat est une étendue parent (l'étendue d'origine) et une étendue enfant (l'étendue que vous avez créée). ...
À moins que vous ne rendiez explicitement les éléments privés, les éléments de la portée parent sont disponibles pour la portée enfant. Toutefois, les éléments que vous créez et modifiez dans la portée enfant n'affectent pas la portée parent, sauf si vous spécifiez explicitement la portée lorsque vous créez les éléments.
Mais quand j'essaie ce qui suit:
PS> $Name = "John" PS> Powershell.exe PS>echo $Name // No Output
Il semble d'après la citation ci-dessus que le "démarrage d'une nouvelle instance de powershell" est une portée enfant, donc tous les éléments de la portée parent doivent y être visibles. Quelqu'un peut-il expliquer?
Réponses
Pour compléter la réponse utile d'iRon :
- [...] devrons-nous spécifier explicitement le modificateur local ?
$local:est rarement requis , car la portée locale est implicite en l'absence d'un spécificateur de portée.
Cependant, cela ne s'applique que si la variable référencée existe réellement en tant que variable locale , étant donné que la portée dynamique de PowerShell rend également les variables des portées ancestrales (parentes) visibles pour les portées descendantes (enfant) (voir cette réponse pour plus d'informations):
Par exemple, supposons que vous avez
$foo = 'bar'déclaré dans la portée globale , puis faire référence à$foodans un script rechercherait d' abord une instance locale ;$foos'il n'y en a pas, un$foodéfini dans une portée ancestrale (parent) est utilisé, le cas échéant, qui serait le global$foodans cet exemple, et'bar'serait retourné.En revanche, si, dans votre script, vous utilisez
$local:foo, sans qu'une$foovariable locale soit définie, vous obtenez$nullpar défaut ou, siSet-StrictMode -Version 2ou supérieur est en vigueur, une erreur de fin d' instruction se produit.
- MSDN dit : [...] en créant une session, ou en démarrant une nouvelle instance de PowerShell [...] le résultat est une portée parent (la portée d'origine) et une portée enfant (la portée que vous avez créée).
La documentation est incorrecte à cet égard au moment de la rédaction de cet article (un problème GitHub a été signalé) :
Les relations ancestrales (parent-enfant) entre les portées n'existent que dans le contexte d'une session donnée (espace d'exécution) .
Autrement dit, la portée dynamique - la visibilité des variables et autres définitions des portées ancestrales - ne s'applique qu'aux portées d'une session donnée.
Une exception notable est que les fonctions d'un module ne s'exécutent pas dans une portée enfant de la portée appelante - sauf si cette portée appelante se trouve être la portée globale ; les modules ont leurs propres domaines de portée (appelés techniquement états de session ) qui sont liés à la portée globale uniquement - voir ce problème de documentation GitHub pour une discussion.
Par conséquent, aucune portée enfant de la portée appelante n'est créée dans les scénarios suivants, où le code nouvellement lancé ne sait rien des variables (et autres définitions) dans la portée appelante :
Démarrage d'une nouvelle session via la communication à distance PowerShell (par exemple, avec Enter-PSSession) ouInvoke-Command -Computer
Démarrage d'une tâche [thread] en arrière -plan avec Start-Jobou Start-ThreadJobou exécution de threads en parallèle avec ForEach-Object -Paralleldans v7.0+
Démarrage d'une nouvelle instance PowerShell (processus) à l'aide de l' interface de ligne de commande PowerShell (
pwshpour PowerShell [Core],powershell.exepour Windows PowerShell).Pour communiquer les valeurs de la portée appelante au code nouvellement lancé dans ces scénarios, une action explicite est requise :
- Lors de l'appel de la CLI ou de l'utilisation de
Start-Job, lorsqu'un processus enfant sur la même machine est créé, seules les variables d' environnement définies dans le processus appelant deviennent automatiquement disponibles pour le processus enfant. - Sinon, les valeurs de l'appelant doivent être transmises en tant qu'arguments ou - sauf lors de l'utilisation de la CLI - via la
$using:portée - voir cette réponse .
- Lors de l'appel de la CLI ou de l'utilisation de
A commencer par la dernière question :
Les portées entrent en jeu avec des fonctions et des scripts invoqués (applet de commande), comme :
Function Test {
$Test++ Write-Host 'Local:' $Test
}
$Test = 5 Test Write-Host 'Global:' $Test
Retour:
Local: 6
Global: 5
Et:
Function Test {
$Global:Test++ Write-Host 'Local:' $Test
}
$Test = 5 Test Write-Host 'Global:' $Test
Retour:
Local: 6
Global: 6
Ou si vous mettez la fonction dans un script (par exemple MyScript.ps1):
$Test = 5 .\MyScript.ps1 Write-Host $Test # $Test is unaffected unless you use the $Global scope in your script
Ce qui renverra essentiellement les mêmes résultats que ci-dessus, à moins que vous ne dot-sourcez votre script où il s'exécutera dans la portée actuelle :
$Test = 5 . .\MyScript.ps1 Write-Host $Test # $Test might be affected by MyScript.ps1 if you just use $Test
Pour ce que vous faites :
vous créez une nouvelle session PowerShell complète (avec Powershell.exe) qui commencera avec une nouvelle liste de variables.
Notez ici que vous verrez à nouveau les variables initiales si vous exitdepuis la nouvelle session :
PS C:\> $Name = "John" PS C:\> Powershell.exe Windows PowerShell Copyright (C) Microsoft Corporation. All rights reserved. Try the new cross-platform PowerShell https://aka.ms/pscore6 PS C:\> Write-Host 'New session' $Name
New session
PS C:\> Exit
PS C:\> Write-Host 'Initial session' $Name
Initial session John
En ce qui concerne la première question, je ne pense pas qu'il existe de nombreuses applications où vous devez explicitement vous référer à la $Localportée, mais pour vous donner un exemple où vous pourriez l'utiliser :
$Test = 5 Function Test { Write-Host ($Local:Test++)
}
Test
Dans l'exemple ci-dessus, l' opérateur d' incrémentation unaire commencera par 0si vous utilisez explicitement la $Localportée (en fait, vous commencez par une variable locale vide qui sera convertie en 0) et par 5si vous omettez la $Localportée où vous hériterez d'une copie de la $Testvariable sur la portée parente.