PowerShell Scopes: Begrip van Variabele Scope

Wanneer je een PowerShell-script schrijft zonder functies en zonder externe afhankelijkheden van andere scripts, is het concept van PowerShell-scopes niet echt van belang. Het concept van PowerShell-globale variabelen staat niet centraal. Maar wanneer je begint met het bouwen van functies, modules en leert om scripts vanuit andere scripts aan te roepen, wordt het onderwerp belangrijker.

In dit artikel ga je een diepgaande les krijgen in wat scopes in PowerShell zijn, hoe ze werken, en hoe je code kunt schrijven met scopes in gedachten. Tegen de tijd dat je klaar bent, zul je PowerShell’s globale variabelen begrijpen en nog veel meer!

Scopes: Een Beetje Zoals Emmer

Heb je ooit een script geschreven waarbij je een variabele definieert en wanneer je de waarde van die variabele controleert, het iets anders is? Je kunt je afvragen hoe die variabele is veranderd terwijl je hem duidelijk hebt gedefinieerd. Een reden hiervoor kan zijn dat de waarde van de variabele wordt overschreven in een andere scope.

Misschien heb je je afgevraagd hoe bepaalde PowerShell-variabelen waarden hebben wanneer je ernaar verwijst in je console maar niet bestaan in je scripts. Waarschijnlijk bevinden die variabelen zich in een andere ‘emmer’ die op dat moment niet beschikbaar is.

Scopes zijn als emmers. Scopes beïnvloeden de manier waarop PowerShell variabelen, aliassen, functies en PSDrives isoleert tussen verschillende gebieden. Een scope is als een emmer. Het is een plaats om al deze items samen te brengen.

Wanneer PowerShell start, maakt het automatisch deze “emmers” voor je aan. Op dat moment gebruik je al scopes zonder dat je het beseft. Alle scopes worden door PowerShell gedefinieerd en worden gemaakt zonder enige hulp van jouw kant.

Bereikstypes

Wanneer PowerShell wordt opgestart, maakt het automatisch vier “buckets” of bereiken aan waarin verschillende items kunnen worden geplaatst. Je kunt geen bereiken zelf maken. Je kunt alleen items toevoegen en verwijderen uit deze hieronder gedefinieerde bereiken.

Globaal Bereik

Items die worden gedefinieerd wanneer PowerShell wordt geopend, worden ingesteld op het globale bereik. Deze items omvatten door het systeem gemaakte objecten zoals PowerShell-schijven en ook alles wat je hebt gedefinieerd in een PowerShell-profiel omdat je profiel wordt uitgevoerd bij het opstarten.

Items in het globale bereik zijn overal beschikbaar. Je kunt items in het globale bereik interactief op de console refereren, in elk script dat je uitvoert, en in elke functie. De globale variabelen van PowerShell zijn overal. Om deze reden is het gebruikelijk om PowerShell-globale variabelen tussen scripts te gebruiken.

Er is slechts één globaal bereik dat overal geldt.

Script Bereik

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.

Alleen items die zijn gemaakt in dat specifieke scriptbereik kunnen elkaar refereren.

Privé Bereik

Typisch kan een gedefinieerd item worden benaderd vanuit andere bereiken – niet waar voor items in een privébereik. Items in een privébereik bevatten objecten die verborgen zijn voor andere bereiken. Een privébereik wordt gebruikt om items te maken met dezelfde naam als items in andere bereiken om overlap te voorkomen.

Lokaal Bereik

In tegenstelling tot de andere scopes, is de lokale scope een beetje anders. De lokale scope is een verwijzing naar de globale, script- of private scope. De lokale scope is relatief aan de context waarin de code op dat moment wordt uitgevoerd.

Als je een variabele, alias, functie of PSDrive maakt zonder deze expliciet aan een scope toe te wijzen (wat we later zullen behandelen), komt deze in de lokale scope terecht.

Verwijzen naar Scopes

Nu je een idee hebt van de vier soorten scopes die bestaan, moet je ook weten dat er twee manieren zijn om naar die scopes te verwijzen.

In PowerShell zijn er twee manieren om scopes te refereren – genoemd of genummerd. Beide methoden refereren naar dezelfde scopes, maar gewoon op een andere manier. Dit zijn twee verschillende manieren om met scopes om te gaan.

Genoemde Scopes

Hierboven in de sectie Scope Type heb je geleerd over scopes die worden aangeduid met een naam. Het verwijzen naar een scope op naam wordt intuïtief een genoemde scope genoemd. Het belangrijkste doel van het verwijzen naar een scope op naam is om een item aan een scope toe te wijzen. Je leert hieronder hoe je dit moet doen.

Genummerde Scopes

Samen met een naam heeft elke scope een nummer dat begint bij nul, wat altijd de lokale scope zal zijn. Scopes worden dynamisch genummerd in relatie tot de huidige lokale scope.

Bijvoorbeeld, zodra je een PowerShell-sessie opent, werk je in de globale scope. Op dat moment is de globale scope de lokale scope (denk eraan dat de lokale scope slechts een pointer is).

Aangezien de lokale scope altijd scope nul is, is op dit moment ook de wereldwijde scope scope nul. Maar, wanneer je een script uitvoert vanuit dezelfde sessie, wordt er een scriptscope gemaakt. Tijdens het uitvoeren is de pointer van de lokale scope dan veranderd naar de scriptscope. Nu is de scriptscope scope nul en de wereldwijde scope is scope één.

Scopes zijn genummerd. Dit proces herhaalt zich voor zoveel scopes als je hebt waar de lokale scope 0 is. Scopes worden dynamisch genummerd op basis van de scopeshiërarchie.

Scopeshiërarchie en Erfenis

Zoals eerder vermeld, wanneer je een PowerShell-sessie start, maakt PowerShell enkele items voor je aan in de wereldwijde scope. Deze items kunnen functies, variabelen, aliassen of PSDrives zijn. Alles wat je definieert in je PowerShell-sessie zal ook in de wereldwijde scope worden gedefinieerd.

Aangezien je standaard in de wereldwijde scope zit, als je iets doet dat een andere scope creëert zoals het uitvoeren van een script of het uitvoeren van een functie, zal er een kindscope worden gemaakt met de ouder als de wereldwijde scope. Scopes zijn als processen met ouders en kinderen.

Alles wat is gedefinieerd in een ouderlijke scope, de wereldwijde scope in dit geval, zal toegankelijk zijn in de kindscope. Maar deze items zijn alleen bewerkbaar in de scope waarin ze zijn gedefinieerd.

Laten we zeggen dat je een script hebt genaamd Test.ps1. Binnen dit script staat een enkele regel zoals hieronder weergegeven.

$a = 'Hello world!'

Wanneer je dit script uitvoert, wordt aan $a een waarde toegewezen in de lokale scope (script terwijl het script wordt uitgevoerd). Wanneer Test.ps1 wordt uitgevoerd, kun je hieronder zien dat je er geen verwijzing naar kunt maken nadat het script is uitgevoerd. Omdat $a is toegewezen terwijl in de script-scope, kan de globale scope (bij de interactieve console) het niet zien.

Global scope cannot see the variable

Laten we dit voorbeeld een stap verder nemen en het Test.ps1 script er als volgt uit laten zien. Het script probeert nu de waarde van $a weer te geven voordat het in dezelfde scope wordt ingesteld.

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

Om te demonstreren, wijs een waarde toe aan $a in de interactieve console. Dit wijst de waarde toe op de globale scope. Nu, wanneer het script wordt uitgevoerd, zal het de bovenliggende scope (globaal) erven en de waarde moeten kunnen zien.

Je kunt hieronder zien dat wanneer Test.ps1 wordt uitgevoerd (een child-scope van de globale scope creëert), het de waarde van $a kan zien. Je kunt ook zien dat de waarde van de variabele ook beschikbaar is in de globale scope, aangezien dit de scope is waar het is ingesteld. Dit betekent dat $a beschikbaar is in zowel de script (child) als de parent (global) scopes.

Variable is available in script and global scopes

Onthoud dit gedrag van scope-overerving. Door dit te doen, zal het je helpen bij het oplossen van gevallen waarin variabeelconflicten optreden, zoals variabelen in verschillende scopes met dezelfde naam.

Definiëren en benaderen van items in scopes

Nu je weet wat een scope is en hoe ze werken, hoe kun je er toegang toe krijgen? Laten we eens kijken hoe je PowerShell kunt gebruiken om een variabele scope in te stellen (en er toegang toe te krijgen).

Get/Set-Variable

In PowerShell zijn er twee cmdlets waarmee je variabelen kunt instellen, genaamd Get-Variable en Set-Variable. Met behulp van deze cmdlets kun je de waarde van een variabele ophalen of een waarde definiëren.

Beide cmdlets zijn vergelijkbaar met een Name en Scope parameter. Met behulp van deze parameters kun je in PowerShell variabele waarden instellen en ophalen in alle scopes.

Lokale scopes

Om een variabele in een lokale scope in te stellen, gebruik je Set-Variable en geef je deze een lokale variabelenaam en een waarde zoals hieronder weergegeven.

PS> Set-Variable -Name a -Value 'foo'

De lokale scope is altijd de standaard, dus als je de Scope parameter niet gebruikt, wordt de variabele altijd gedefinieerd in de lokale scope.

Om de waarde van een lokaal gedefinieerde variabele op te halen, gebruik je Get-Variable en geef je deze de naam van de variabele.

PS> Get-Variable -Name a

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

Private/Script/Global scopes

Je gebruikt dezelfde parameters (Name en Value) wanneer je werkt met private, script- en globale variabelen. Het enige verschil is dat je deze keer de Scope parameter gebruikt om expliciet de scope te definiëren.

De methoden voor het instellen van een privaat, script- of globaal gedefinieerde variabele zijn hetzelfde. Vervang eenvoudig de waarde die aan de Scope parameter wordt doorgegeven door Private,  Script  Global.

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

Om de waarde van een script of variabele met een globale scope op te halen, gebruik Get-Variable en geef het de naam en scope.

PS> Get-Variable -Name a -Scope <Script|Global>

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

Opmerking: U kunt ook scopes aanduiden met scope-nummers in plaats van namen met de Get/Set-Variable-cmdlets.

Scope “Voorvoegen”

U kunt ook variabelen in scopes ophalen en instellen met een snelkoppeling. In plaats van PowerShell-cmdlets te gebruiken, voegt u de scope toe aan de variabele bij verwijzingernaar.

Lokale Scopes

Aangezien de lokale scope altijd de standaard is, zal het eenvoudigweg definiëren van een variabele en verwijzen ernaar een lokale scopevariabele instellen en ophalen

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

foo

Privé-/Script-/Globale Scopes

Als u script- of globale scopes wilt definiëren en verwijzen, voegt u de scope-naam en een puntkomma toe aan de variabelen.

Bijvoorbeeld, om de variabele $a in de globale scope in te stellen, voegt u $global: voor a toe.

$global:a = ‘foo’

Hetzelfde kan worden gedaan voor een script-gekoppelde variabele.

$script:a = ‘foo’

Zodra de variabelen zijn ingesteld in de gewenste scope, verwijst u er op dezelfde manier naar. Merk ook op dat u de scope-voorvoegsel kunt uitsluiten als de gedefinieerde scope de lokale scope is.

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

Scopes in Scriptblokken

PowerShell heeft een handige constructie genaamd scriptblokken. Scriptblokken stellen u in staat om fragmenten code te verplaatsen en ze bijna overal uit te voeren.

Net als een PS1-script worden scriptblokken uitgevoerd in hun eigen script-scope. Wanneer u een scriptblok uitvoert, voert u in feite een PS1-script uit.

Merk op in het voorbeeld hieronder waar een variabele wordt gedefinieerd in de globale scope en vervolgens wordt geprobeerd om deze te overschrijven in een script scope. Zoals je hierboven hebt geleerd, werkt dit niet omdat een kindscope geen verwijzing kan maken naar een bovenscope.

A child scope cannot overwrite a parent scope

Dit voorbeeld laat zien dat wanneer $a wordt gewijzigd in het scriptblok, de globale variabelendefinitie voor $a niet wordt gewijzigd omdat het scriptblok een script kindscope is.

Puntbronnen Scripts (lokale scopes omwisselen)

PowerShell heeft een concept genaamd punt-bronnen. Dit is een methode waarmee je een PS1-script kunt uitvoeren en alles wat normaal gesproken script-gespecificeerd zou zijn in plaats daarvan in de lokale scope kunt brengen.

Door een punt (.) voor het refereren naar een PS1-script te plaatsen en het uit te voeren, “punt-bronnen” de inhoud van het script en brengt alles in de lokale scope.

Om dit te demonstreren, heb ik opnieuw een Test.ps1 script dat een variabele definieert zoals hieronder getoond.

$a = 'Hello world!'

In een PowerShell-console, stel een waarde in voor een $a variabele en punt-bron dit script zoals hieronder getoond. Merk op dat de oorspronkelijke variabele werd overschreven. PowerShell heeft de twee lokale scopes “samengevoegd”.

Original variable overwritten

Het gebruik van de AllScope-eigenschap (Optie)

Je hebt gezien hoe je kunt omgaan met items in specifieke scopes, maar tot nu toe is elk item nog steeds gedefinieerd in een enkele scope. Maar wat als je niet weet in welke scope een variabele is gedefinieerd?

Bij het definiëren van een variabele met de Set-Variable cmdlet, kunt u de variabele in één keer in alle scopes plaatsen. Gebruik hiervoor de AllScope-waarde voor de Option-parameter.

Ter illustratie is het script Test.ps1 aangepast om een variabele in alle scopes in te stellen. Die variabele wordt vervolgens hieronder uitvoergegeven.

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

Vervolgens kunt u hieronder zien dat een waarde wordt ingesteld voor $a in de globale scope en dat het script Test.ps1 wordt uitgevoerd. In plaats van geen effect te hebben, is de waarde van $a overschreven. Niet alleen is het gedefinieerd in de scriptscope (Write-Output $a), maar het heeft ook de globale scope overschreven.

Variable overwritten in global scope

De AllScope-optie is handig, maar wees voorzichtig. Deze optie doet in feite afstand van het concept van scopes en voegt alles samen.

Scopes van Functies

Wanneer u een functie uitvoert, bevindt alle code binnen die functie zich in zijn eigen kindscope. Functiescopes volgen hetzelfde ouder/kind-gedrag als andere scopes.

Het hebben van afzonderlijke scopes voor elke functie is een goed idee. Het biedt betere controle over items zonder zich zorgen te hoeven maken over conflicten. Het geeft ook het extra voordeel van geautomatiseerde opschoning van variabelen in een functie. Zodra de functie is voltooid, worden alle in de functie gedefinieerde items gewist.

Om te demonstreren, kopieer/plak de onderstaande functie rechtstreeks in de PowerShell-console.

Function Do-Thing {
    $var = 'bar'
}

Eenmaal geplakt, voer de functie uit. Merk op dat je geen toegang hebt tot de variabele $var buiten de functie.

PS> Do-Thing
PS> $var

Het Privaat Houden van Items (Overerving Uitschakelen)

Normaal gesproken, als een variabele is gedefinieerd in een bovenliggende scope, zal die variabele ook in de onderliggende scope gedefinieerd zijn. Maar misschien wil je een variabelenaam gebruiken die al is gedefinieerd in een van de scopes om in een sessie uit te voeren. In dat geval kun je kiezen voor een andere variabelenaam of de variabele definiëren in een privé-scope, waardoor het een privévariabele wordt.

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

Je kunt hieronder zien dat ik een privé-variabele definieer op het wereldwijde niveau en vervolgens het script Test.ps1 uitvoer. Normaal gesproken is een variabele die in een bovenliggende scope is gedefinieerd, beschikbaar in de onderliggende scope, maar niet bij een privé-variabele.

In het onderstaande voorbeeld kun je zien dat de script-onderliggende scope gecreëerd door het uitvoeren van het script Test.ps1 de privé-variabele $a, gedefinieerd in de bovenliggende scope, niet kon zien.

Script scope cannot see the private-scoped variable

In tegenstelling tot wereldwijde scopes of de AllScope-optie op de Set-Variable-cmdlet, zijn privé-variabelen een uitstekende manier om items te compartimenteren.

Beste Praktijken voor Scope

Denken dat het definiëren van variabelen in het globale bereik of het gebruik van de AllScope-optie de juiste weg is, is gebruikelijk. Immers, alle variabelen zijn overal beschikbaar. Er is geen noodzaak om je zorgen te maken over de complicaties van bereiken. Hoewel dit extra vrijheid biedt voor toegang tot wat is gedefinieerd, kan het snel uit de hand lopen en moeilijk te debuggen worden.

In plaats van te proberen het gebruik van bereiken te voorkomen, volg deze tips:

  1. Specificeer geen bereiken in functies, gebruik parameters om de vereiste informatie naar de functie door te geven.
  2. Blijf zoveel mogelijk binnen het lokale bereik.
  3. In plaats van het definiëren van wereldwijde variabelen vanuit een script, gebruik de Write-Output-cmdlet om alles uit te voeren en sla het op in een variabele wanneer dat nodig is vanuit de console.

Het belangrijkste punt hier is om bereiken te omarmen en te leren ze in je voordeel te gebruiken in plaats van ze te proberen te omzeilen.

Verder lezen

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