Adam the Automator

author
14 minutes, 37 seconds Read

Når du skriver et PowerShell-script uden funktioner og uden eksterne afhængigheder af andre scripts, er begrebet PowerShell-scopes ikke særlig vigtigt. Begrebet PowerShell-globale variabler er ikke foran og i centrum. Men når du begynder at opbygge funktioner, moduler og lærer at kalde scripts fra andre scripts, bliver emnet vigtigere.

I denne artikel får du en dybdegående lektion i, hvad scopes i PowerShell er, hvordan de fungerer, og hvordan du kan skrive kode med scopes i tankerne. Når du er færdig, vil du forstå PowerShells globale variabler og meget mere!

Indholdsfortegnelse

Scopes: Lidt ligesom spande

Har du nogensinde skrevet et script, hvor du definerer en variabel, og når du kontrollerer værdien af denne variabel, er det noget andet? Du klør dig måske i hovedet over, hvordan denne variabel ændrede sig, da du klart definerede den. En af grundene kan være, at variablens værdi bliver overskrevet i et andet anvendelsesområde.

Måske har du undret dig over, hvordan visse PowerShell-variabler har værdier, når du refererer til dem i konsollen, men ikke eksisterer i dine scripts. Der er stor sandsynlighed for, at disse variabler er i en anden “spand”, som ikke er tilgængelig på det pågældende tidspunkt.

Scopes er ligesom spande. Scopes påvirker den måde, hvorpå PowerShell isolerer variabler, aliaser, funktioner og PSDrivere mellem forskellige områder. Et scope er som en spand. Det er et sted, hvor alle disse elementer kan samles.

Når PowerShell starter, opretter den automatisk disse “spande” for dig. På det tidspunkt bruger du allerede scopes uden at være klar over det. Alle scopes defineres af PowerShell og bliver oprettet uden hjælp fra din side.

Scope-typer

Når PowerShell starter op, opretter den automatisk fire “spande” eller scopes til forskellige elementer, som de skal placeres i. Du kan ikke oprette scopes på egen hånd. Du kan kun tilføje og fjerne elementer fra disse scopes, der er defineret nedenfor.

Globalt scope

Elementer, der er defineret, når PowerShell åbnes, er indstillet på det globale scope. Disse elementer omfatter systemskabte objekter som PowerShell-drev og også alt, hvad du har defineret i en PowerShell-profil, da din profil kører ved opstart.

Etems i det globale omfang er tilgængelige overalt. Du kan henvise til elementer i det globale omfang interaktivt på konsollen, i ethvert script, du kører, og i enhver funktion. PowerShells globale variabler er tilgængelige overalt. Derfor er den almindelige anvendelse af PowerShells globale variabler at bruge PowerShells globale variabler mellem scripts.

Der er kun ét globalt anvendelsesområde, der styrer overordnet.

Script Scope

Et script scope oprettes automatisk, hver gang et PowerShell-script køres. Du kan have mange forskellige script scope-instanser. Script scopes oprettes, når du f.eks. udfører et PS1-script eller et modul.

Kun elementer, der er oprettet i den pågældende script scope-instans, kan referere til hinanden.

Private Scope

Typisk kan et defineret element tilgås fra andre scopes – det gælder ikke med elementer i et privat scope. Elementer i et privat scope, indeholder objekter, der er skjult for andre scopes. Et privat scope bruges til at oprette elementer med samme navn som elementer i andre scopes for at forhindre overlapning.

Local Scope

I modsætning til de andre scopes er det lokale scope en smule anderledes. Det lokale scope er en pegepind til det globale, script- eller private scope. Det lokale scope er relativt i forhold til den kontekst, som koden kører i på det pågældende tidspunkt.

Hvis du opretter en variabel, et alias, en funktion eller et PSDrive uden at tildele den eksplicit et scope (hvilket vi kommer ind på senere), vil den gå ind i det lokale scope.

Henvisning til scopes

Nu, hvor du har en idé om de fire typer scopes, der findes, bør du også vide, at der er to måder at henvise til disse scopes på.

I PowerShell er der to måder at henvise til scopes på – navngivne eller nummererede. Begge metoder refererer til de samme scopes, men blot på en anden måde. Det er to forskellige måder at interagere med scopes på.

Navngivne scopes

Overfor i afsnittet om scoptype lærte du om scopes, der refereres ved navn. At henvise til et scope ved navn kaldes intuitivt set et navngivet scope. Hovedformålet med at henvise til et scope ved navn er at tildele et element til et scope. Du lærer, hvordan du gør dette nedenfor.

Nummererede scopes

Sammen med et navn har hvert scope et nummer, der starter ved nul, som altid vil være det lokale scope. Scopes nummereres dynamisk i forhold til det aktuelle lokale scope.

Til eksempel, så snart du åbner en PowerShell-session, opererer du i det globale scope. På dette tidspunkt er det globale scope det lokale scope (husk, at det lokale scope blot er en pegepind).

Da det lokale scope altid er scope nul, er det globale scope på dette tidspunkt også i øjeblikket scope nul. Men når du kører et script fra den samme session, oprettes der et scriptområde. Når du kører, er den lokale scope-pointer derefter ændret til script scope. Nu er script scope er scope nul, og det globale scope er scope et.

Scopes er nummereret Denne proces gentages for så mange scopes, som du har, hvor det lokale scope er 0. Scopes er dynamisk nummereret efter scope-hierarkiet.

Områdeshierarki og arvelighed

Som tidligere nævnt opretter PowerShell nogle elementer til dig i det globale anvendelsesområde, når du starter en PowerShell-session. Disse elementer kan være funktioner, variabler, aliaser eller PSDrivere. Alt, hvad du definerer i din PowerShell-session, vil også blive defineret i det globale scope.

Da du som standard befinder dig i det globale scope, vil der, hvis du gør noget, der skaber et andet scope som f.eks. at udføre et script eller køre en funktion, blive oprettet et underscope med det overordnede scope som det globale scope. Scopes er ligesom processer med forældre og børn.

Alt, der er defineret i et overordnet scope, i dette tilfælde det globale scope, vil være tilgængeligt i det underordnede scope. Men disse elementer kan kun redigeres i det scope, de blev defineret i.

Lad os sige, at du har et script, der hedder Test.ps1. Inde i dette script er der en enkelt linje som vist nedenfor.

$a = 'Hello world!'

Når du kører dette script, tildeles $a en værdi i det lokale scope (scriptet, mens scriptet kører). Når Test.ps1 køres, kan du se nedenfor, at du ikke kan henvise til den, efter at scriptet er blevet udført. Da $a blev tildelt, mens den var i scriptområdet, kan det globale område (på den interaktive konsol) ikke se den.

Globalt område kan ikke se variablen

Lad os tage dette eksempel et skridt videre og få scriptet Test.ps1 til at se ud som nedenfor. Scriptet forsøger nu at udstede værdien af $a, før det indstiller den i det samme scope.

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

For at demonstrere dette tildeler du en værdi til $a på den interaktive konsol. Dette tildeler værdien i det globale omfang. Når scriptet nu køres, vil det arve det overordnede scope (global) og burde kunne se værdien.

Du kan se nedenfor, at når Test.ps1 udføres (og der oprettes et underscope af det globale scope), kan det se værdien af $a. Du kan også se, at variablens værdi også er tilgængelig i det globale scope, da det er i dette scope, den blev indstillet. Det betyder, at $a er tilgængelig både i script (child) og parent (global) scopes.

Variabel er tilgængelig i script og global scopes

Husk denne scope-arveadfærd. Ved at gøre det vil det hjælpe dig ved fejlfinding, når der opstår variabelkonflikter, f.eks. variabler i forskellige scopes med samme navn.

Definition af og adgang til elementer i scopes

Nu da du ved, hvad et scope er, og hvordan de fungerer, hvordan får du så adgang til dem? Lad os se, hvordan du bruger PowerShell til at indstille et variabelt anvendelsesområde (og få adgang til dem).

Get/Set-Variable

I PowerShell er der to cmdlets, som giver dig mulighed for at indstille variabler, der hedder Get-Variable og Set-Variable. Disse cmdlets giver dig mulighed for at hente værdien af en variabel eller definere en værdi.

Både cmdlets ligner hinanden med en Name– og Scope-parameter. Ved hjælp af disse parametre giver PowerShell dig mulighed for at angive og hente variabelværdier på tværs af alle scopes.

Lokale scopes

For at angive en variabel i et lokalt scope skal du bruge Set-Variable og give den et lokalt variabelnavn og en værdi som vist nedenfor.

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

Det lokale scope er altid standard, så hvis du ikke bruger Scope-parameteren, vil variablen altid blive defineret i det lokale scope.

For at hente værdien af en variabel med lokalt omfang skal du bruge Get-Variable, der giver den navnet.

PS> Get-Variable -Name aName Value---- -----a foo

Private/Script/Globale områder

Du bruger også de samme parametre (Name og Value), når du arbejder med private, script- og globale variabler. Den eneste forskel er, at du denne gang bruger Scope-parameteren til eksplicit at definere omfanget.

Metoderne til at indstille en privat, script- eller global variabel med omfang er de samme. Du skal blot erstatte den værdi, der er overgivet til Scope-parameteren som Private, Script Global.

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

For at hente værdien af en script- eller globalt omfangsbegrænset variabel skal du bruge Get-Variable og give den navnet og omfanget.

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

Bemærk: Du kan også referere til scopes ved hjælp af scope-numre i stedet for navne med cmdlets Get/Set-Variable.

Scope “Prefacing”

Du kan også hente og angive variabler i scopes ved hjælp af en genvej. I stedet for at bruge PowerShell cmdlets, skal du præfacere scope til variablen, når du henviser til den.

Lokale scopes

Da det lokale scope altid er standard, skal du blot definere en variabel og foretage en henvisning for at indstille og hente en variabel i et lokalt scope

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

Private/Script/Globale scopes

Hvis du vil definere og henvise til script- eller globale scopes, kan du indlede variablerne med scope-navnet og et semikolon.

For at indstille variablen $a i det globale scope kan du f.eks. indlede a med $global:.

$global:a = 'foo'

Det samme kan gøres for en variabel med script-scope.

$script:a = 'foo'

Når variablerne er indstillet i det foretrukne scope, skal du referere til dem på samme måde. Bemærk også, at du kan udelukke scope-præferencen, hvis det definerede scope er det lokale scope.

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

Scopes i scriptblokke

PowerShell har en praktisk konstruktion, der hedder scriptblokke. Scriptblokke giver dig mulighed for at flytte rundt på kodestumper og udføre dem stort set hvor som helst.

Som et PS1-script kører scriptblokke i deres eget scriptområde. Når du udfører en scriptblok, udfører du i princippet et PS1-script.

Bemærk i eksemplet nedenfor, hvor en variabel er defineret i det globale omfang og derefter forsøgt overskrevet i et scriptområde. Som du lærte ovenfor, vil dette ikke fungere, fordi et underordnede scope ikke kan referere til et overordnet scope.

Et underordnet scope kan ikke overskrive et overordnet scope

Dette eksempel viser, at når $a ændres i scriptblokken, ændres den globale variabeldefinition for $a ikke, fordi scriptblokken er et scriptunderscope.

Dot Sourcing-scripts (ombytning af lokale scopes)

PowerShell har et koncept, der kaldes dot-sourcing. Det er en metode, der giver dig mulighed for at udføre et PS1-script og bringe alt, der ville være script-scoped, ind i det lokale scope i stedet.

Gennem at sætte et punktum (.) før henvisning til et PS1-script og udføre det, “dot-sourcer” indholdet af scriptet og bringer alt ind i det lokale scope.

For at demonstrere har jeg igen et test.ps1-script, som definerer en variabel som vist nedenfor.

$a = 'Hello world!'

I en PowerShell-konsol skal du angive en værdi for en $a-variabel og derefter dot source dette script som vist nedenfor. Bemærk, at den oprindelige variabel blev overskrevet. PowerShell “smeltede” de to lokale scopes sammen.

Originalvariabel overskrevet

Brug af AllScope-egenskaben (valgmulighed)

Du har set, hvordan du kan interagere med elementer i bestemte scopes, men indtil videre er hvert element stadig defineret i et enkelt scope. Men hvad nu, hvis du ikke ved, hvilket scope en variabel er defineret i?

Når du definerer en variabel med cmdletten Set-Variable, kan du placere variablen i alle scopes på én gang. Det gør du ved at bruge AllScope-værdien for Option-parameteren.

For at demonstrere dette er scriptet Test.ps1 blevet ændret til at angive en a-variabel i alle scopes. Denne variabel udskrives derefter som vist nedenfor.

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

Du kan derefter se nedenfor, at der er fastsat en værdi for $a i det globale område, og scriptet Test.ps1 udføres. Men i stedet for at være uden virkning er $as værdi blevet overskrevet. Ikke alene er den blevet defineret i scriptområdet (Write-Output $a), men den er også overskrevet i det globale område.

Variabel overskrevet i det globale område

Muligheden AllScope er praktisk, men vær forsigtig. Denne indstilling fjerner i det væsentlige begrebet scopes og slår alting sammen.

Funktionsscopes

Når du udfører en funktion, er al kode inden for denne funktion i sit eget underordnede scope. Funktionsscopes følger den samme child/parent-adfærd som andre scopes.

Det er en god idé at have separate scopes for hver funktion. Det giver bedre kontrol over elementer uden at skulle bekymre sig om elementer, der er i konflikt med hinanden, Det giver også den ekstra fordel af automatiseret oprydning af variabler i en funktion. Så snart funktionen er afsluttet, bliver alle de elementer, der er defineret i funktionen, ryddet.

For at demonstrere kan du kopiere/indsætte nedenstående funktion direkte i PowerShell-konsollen.

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

Når funktionen er indsat, skal du udføre den. Bemærk, at du ikke kan få adgang til $var-variablen uden for funktionen.

PS> Do-ThingPS> $var

Holding Items Private (Disabling Inheritance)

Typisk set vil en variabel, der er defineret i et overordnet anvendelsesområde, blive defineret i det underordnede anvendelsesområde. Men måske vil du gerne bruge et variabelnavn, men dette variabelnavn er allerede blevet defineret i et af de scopes, der skal køres i en session. I så fald kan du enten vælge et andet variabelnavn eller definere variablen i et privat scope, hvilket gør den til en privat variabel.

En måde at bruge scopes på for at reducere konflikter er ved at bruge det private scope. Ved at bruge det private scope deaktiveres arv på den pågældende specifikke variabel. Når der oprettes flere underordnede scopes, vil disse underordnede scopes ikke se nogen variabler, der er defineret i et privat scope.

Et script Test.ps1 udsender værdien af $a som vist nedenfor.

Write-Output $a

Du kan se nedenfor, at jeg definerer en variabel med privat scope i det globale scope og derefter udfører scriptet Test.ps1. Når du definerer en variabel i et overordnet scope, vil den pågældende variabel normalt være tilgængelig i det underordnede scope – det er ikke tilfældet med en privat scope-variabel.

I nedenstående eksempel kan du se, at det underordnede scriptområde, der blev oprettet ved at udføre scriptet Test.ps1, ikke kunne se den private $a-variabel, der er defineret i det overordnede område.

Script scope kan ikke se den privatskoperede variabel

I modsætning til globale scopes eller AllScope-indstillingen på cmdlet Set-Variable er privatskoperede variabler en glimrende måde at opdele elementer på.

Bedste praksis for scoping

Det er almindeligt at tro, at det er bedst at definere variabler i det globale omfang eller bruge AllScope-indstillingen. Alle variabler er jo trods alt tilgængelige overalt. Der er ingen grund til at bekymre sig om komplikationerne ved scopes. Selv om det giver ekstra frihed til adgang til det, der er defineret, kan det hurtigt løbe løbsk og blive svært at fejlfinde.

I stedet for at forsøge at forhindre brugen af scopes følger disse tips:

  1. I stedet for at angive scopes i funktioner skal du bruge parametre til at videregive de nødvendige oplysninger til funktionen.
  2. Hold dig så vidt muligt inden for det lokale scope.
  3. I stedet for at definere globale variabler fra et script skal du bruge cmdlet Write-Output til at outputte alt og gemme det til en variabel, når det er nødvendigt fra konsollen.

Den vigtigste pointe her er at tage scopes til sig og lære at bruge dem til din fordel i stedet for at forsøge at omgå dem.

Yderligere læsning

  • Om avancerede funktioner og funktionsparametre
  • Anvendelse af miljøvariabler i PowerShell

Similar Posts

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.