Adam the Automator

author
15 minutes, 17 seconds Read

När du skriver ett PowerShell-skript utan funktioner och utan externa beroenden av andra skript är begreppet PowerShell-områden inte särskilt viktigt. Begreppet PowerShell globala variabler är inte framträdande. Men när du börjar bygga funktioner, moduler och lär dig att anropa skript från andra skript blir ämnet viktigare.

I den här artikeln kommer du att få en djupgående lektion i vad scopes i PowerShell är, hur de fungerar och hur du kan skriva kod med scopes i åtanke. När du är klar kommer du att förstå PowerShells globala variabler och mycket mer!

Innehållsförteckning

Scopes: Har du någonsin skrivit ett skript där du definierar en variabel och när du kontrollerar värdet på variabeln är det något annat? Du kanske kliar dig i huvudet över hur variabeln ändrades när du tydligt definierade den. En anledning kan vara att variabelns värde skrivs över i ett annat scope.

Kanske har du undrat hur vissa PowerShell-variabler har värden när du hänvisar till dem i konsolen men inte existerar i dina skript. Det är troligt att dessa variabler finns i en annan ”hink” som inte är tillgänglig för tillfället.

Områden är som hinkar. Scopes påverkar hur PowerShell isolerar variabler, alias, funktioner och PSDrives mellan olika områden. Ett scope är som en hink. Det är en plats för att samla alla dessa objekt tillsammans.

När PowerShell startar skapar den automatiskt dessa ”hinkar” åt dig. Vid den tidpunkten använder du redan scopes utan att inse det. Alla scope definieras av PowerShell och skapas utan någon hjälp från din sida.

Scope-typer

När PowerShell startar skapar den automatiskt fyra ”hinkar” eller scopes för att olika objekt ska kunna placeras i dem. Du kan inte skapa scopes på egen hand. Du kan bara lägga till och ta bort objekt från dessa scope som definieras nedan.

Global Scope

Engagemang som definieras när PowerShell öppnas ställs in i det globala scope. Dessa objekt inkluderar systemskapade objekt som PowerShell-enheter och även allt du har definierat i en PowerShell-profil eftersom profilen körs vid start.

Engagemang i det globala omfånget är tillgängliga överallt. Du kan referera objekt i det globala omfånget interaktivt på konsolen, i alla skript du kör och i alla funktioner. PowerShells globala variabler finns överallt. Därför är den vanligaste användningen av PowerShells globala variabler att använda PowerShells globala variabler mellan skript.

Det finns bara ett globalt scope som styr övergripande.

Skriptomfång

Ett skriptomfång skapas automatiskt varje gång ett PowerShell-skript körs. Du kan ha många olika instanser av skriptomfång. Skriptomfång skapas till exempel när du kör ett PS1-skript eller en modul.

Endast objekt som skapats i den specifika skriptomfångsinstansen kan referera till varandra.

Privat scope

Typiskt sett kan man få tillgång till ett definierat objekt från andra scope – detta gäller inte för objekt i ett privat scope. Objekt i ett privat scope innehåller objekt som är dolda för andra scopes. Ett privat scope används för att skapa objekt med samma namn som objekt i andra scopes för att förhindra överlappning.

Local Scope

Till skillnad från de andra scopes är det lokala scope lite annorlunda. Det lokala scope är en pekare till det globala, skript- eller privata scope. Det lokala scope är relativt till den kontext som koden körs i vid tillfället.

Om du skapar en variabel, ett alias, en funktion eller PSDrive utan att uttryckligen tilldela den ett scope (vilket vi kommer att behandla senare) kommer den att hamna i det lokala scope.

Referera scope

Nu när du har en uppfattning om de fyra typerna av scope som finns bör du också veta att det finns två sätt att referera dessa scope.

I PowerShell finns det två sätt att referera scope – namngivet eller numrerat. Båda metoderna refererar till samma scope men helt enkelt på ett annat sätt. Det är två olika sätt att interagera med scope.

Named Scopes

Ovan i avsnittet Scope Type lärde du dig om scope som refereras med namn. Att referera ett scope med namn kallas intuitivt för ett namngivet scope. Huvudsyftet med att referera ett scope med namn är att tilldela ett objekt till ett scope. Du lär dig hur du gör detta nedan.

Numrerade scope

Samtidigt med ett namn har varje scope ett nummer som börjar vid noll och som alltid kommer att vara det lokala scope. Scopes numreras dynamiskt i förhållande till det aktuella lokala scope.

Till exempel så snart du öppnar en PowerShell-session arbetar du i det globala scope. Vid denna tidpunkt är det globala tillämpningsområdet det lokala tillämpningsområdet (kom ihåg att det lokala tillämpningsområdet bara är en pekare).

Då det lokala tillämpningsområdet alltid är tillämpningsområde noll, är det globala tillämpningsområdet vid denna tidpunkt också för närvarande tillämpningsområde noll. Men när du kör ett skript från samma session skapas ett skriptomfång. Vid körning har pekaren för det lokala scopeet då ändrats till scriptet scope. Nu är skriptomfånget scope noll och det globala scope är scope ett.

Omfattningarna numreras Denna process upprepas för så många scope som du har där det lokala scope är 0. Scopes numreras dynamiskt enligt scope-hierarkin.

Områdeshierarki och arv

Som tidigare nämnts skapar PowerShell, när du startar en PowerShell-session, några objekt åt dig i det globala omfånget. Dessa objekt kan vara funktioner, variabler, alias eller PSDrives. Allt du definierar i din PowerShell-session kommer också att definieras i det globala scope.

Då du som standard befinner dig i det globala scope, om du gör något som skapar ett annat scope, som att exekvera ett skript eller köra en funktion, kommer ett underscope att skapas med det globala scope som överordnat. Scopes är som processer med föräldrar och barn.

Allt som definieras i ett överordnat scope, det globala scope, i det här fallet, kommer att vara tillgängligt i det underordnade scopet. Men dessa objekt kan bara redigeras i det scope de definierades i.

Säg att du har ett skript som heter Test.ps1. Inne i det här skriptet finns en enda rad som visas nedan.

$a = 'Hello world!'

När du kör det här skriptet tilldelas $a ett värde i det lokala scope (skriptet medan skriptet körs). När Test.ps1 körs kan du se nedan att du inte kan referera till det efter att skriptet har utförts. Eftersom $a tilldelades medan den var i skriptomfånget kan det globala omfånget (vid den interaktiva konsolen) inte se den.

Globalt omfång kan inte se variabeln

Låtsas att vi tar det här exemplet ett steg längre och gör så att skriptet Test.ps1 ser ut som nedan. Skriptet försöker nu att skriva ut värdet på $a innan det fastställs i samma scope.

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

För att demonstrera detta tilldelar du ett värde till $a i den interaktiva konsolen. Detta tilldelar värdet i det globala scope. När skriptet nu körs kommer det att ärva det överordnade scope (global) och bör kunna se värdet.

Du kan se nedan att när Test.ps1 exekveras (vilket skapar ett underordnat scope till det globala scope) kan det se värdet för $a. Du kan också se att variabelns värde är tillgängligt i det globala scope också eftersom det är i detta scope som det har ställts in. Detta innebär att $a är tillgänglig både i skriptet (barn) och i det överordnade (globala) scopet.

Variabeln är tillgänglig i skriptet och i det globala scopet

Håll dig till det här beteendet med scope-arv. Detta hjälper dig vid felsökning när variabelkonflikter uppstår, t.ex. variabler i olika scope med samma namn.

Definition av och åtkomst till objekt i scope

Nu när du vet vad ett scope är och hur de fungerar, hur får du tillgång till dem? Låt oss se hur du använder PowerShell för att ställa in ett variableringsområde (och få tillgång till dem).

Get/Set-Variable

I PowerShell finns det två cmdlets som gör det möjligt att ställa in variabler som heter Get-Variable och Set-Variable. Med dessa cmdlets kan du hämta värdet på en variabel eller definiera ett värde.

Båda cmdlets liknar varandra med en Name och Scope parameter. Med hjälp av dessa parametrar kan du med PowerShell ställa in och hämta variabelvärden i alla scope.

Lokala scope

För att ställa in en variabel i ett lokalt scope använder du Set-Variable och ger den ett lokalt variabelnamn och ett värde enligt nedan.

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

Det lokala scope är alltid standard, så om du inte använder parametern Scope kommer variabeln alltid att definieras i det lokala scope.

För att hämta värdet på en variabel med lokalt scope använder du Get-Variable och ger den namnet.

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

Privata/Script/Globala scope

Du använder samma parametrar (Name och Value) när du arbetar med privata, script- och globala variabler också. Den enda skillnaden är att du den här gången använder parametern Scope för att explicit definiera räckvidden.

Metoderna för att ställa in en variabel med privat, skript eller globalt räckvidd är desamma. Ersätt helt enkelt värdet som skickas till parametern Scope som Private, Script Global.

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

För att hämta värdet på en skript- eller globalt skopad variabel använder du Get-Variable och ger den namn och scope.

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

Notera: Du kan också referera till scope med scope-nummer i stället för namn med cmdlets Get/Set-Variable.

Scope ”Prefacing”

Du kan också hämta och ställa in variabler i scopes med hjälp av en genväg. Istället för att använda PowerShell cmdlets prefacerar du scope till variabeln när du hänvisar till den.

Lokala scope

Då det lokala scope alltid är standard, räcker det med att definiera en variabel och referera för att ställa in och hämta en variabel i ett lokalt scope

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

Privata/Script/Globala scope

Om du vill definiera och referera script- eller globala scope kan du inleda variablerna med scope-namnet och ett semikolon.

För att ställa in variabeln $a i det globala omfånget kan du till exempel inleda a med $global:.

$global:a = 'foo'

Det samma kan göras för en variabel i skriptomfånget.

$script:a = 'foo'

När variablerna har ställts in i det föredragna omfånget hänvisar du sedan till dem på samma sätt. Lägg också märke till att du kan utesluta räckviddsförordet om det definierade räckvidden är den lokala räckvidden.

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

Räckvidder i skriptblock

PowerShell har en praktisk konstruktion som kallas skriptblock. Med hjälp av skriptblock kan du flytta runt kodstycken och köra dem nästan var som helst.

Likt ett PS1-skript körs skriptblock i ett eget skriptområde. När du kör ett skriptblock kör du i princip ett PS1-skript.

Märk i exemplet nedan att en variabel definieras i det globala området och sedan försöker skrivas över i ett skriptområde. Som du lärde dig ovan fungerar detta inte eftersom ett underordnat scope inte kan referera till ett överordnat scope.

Ett underordnat scope kan inte skriva över ett överordnat scope

Det här exemplet visar att när $a ändras i skriptblocket ändras inte den globala variabeldefinitionen för $a eftersom skriptblocket är ett underordnat script scope.

Dot Sourcing Scripts (byte av lokala scope)

PowerShell har ett koncept som kallas dot-sourcing. Det är en metod som gör att du kan exekvera ett PS1-skript och föra in allt som skulle vara skript-skaligt i det lokala tillämpningsområdet istället.

Då du sätter en punkt (.) innan du refererar till ett PS1-skript och exekverar det, ”punktsourcar” du innehållet i skriptet och för in allt i det lokala tillämpningsområdet.

För att demonstrera har jag återigen ett skript Test.ps1 som definierar en variabel enligt nedan.

$a = 'Hello world!'

I en PowerShell-konsol anger du ett värde för en $a-variabel och punktkällar sedan det här skriptet enligt nedan. Lägg märke till att den ursprungliga variabeln har skrivits över. PowerShell ”sammanfogade” de två lokala scopen tillsammans.

Originalvariabel överskriven

Användning av AllScope-egenskapen (alternativ)

Du har sett hur du kan interagera med objekt i specifika scope, men än så länge är varje objekt fortfarande definierat i ett enda scope. Men vad händer om du inte vet vilket scope en variabel är definierad i?

När du definierar en variabel med cmdlet Set-Variable kan du placera variabeln i alla scopes samtidigt. För att göra detta använder du värdet AllScope för parametern Option.

För att demonstrera detta har skriptet Test.ps1 ändrats för att ställa in en a-variabel i alla scope. Den variabeln skrivs sedan ut enligt nedan.

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

Du kan då se nedan att ett värde sätts för $a i det globala området och att skriptet Test.ps1 exekveras. Istället för att inte ha någon effekt har dock $as värde skrivits över. Det har inte bara definierats i skriptomfånget (Write-Output $a) utan det har också skrivits över i det globala omfånget.

Variabeln skrivs över i det globala omfånget

Objektet AllScope är praktiskt men var försiktig. Det här alternativet tar i princip bort begreppet scope och slår ihop allting.

Funktionsomfång

När du exekverar en funktion befinner sig all kod inom den funktionen i sitt eget underordnade scope. Funktionsomfång följer samma child/parent-beteende som andra omfång.

Att ha separata omfång för varje funktion är en bra idé. Det ger bättre kontroll över objekt utan att behöva oroa sig för objekt som står i konflikt med varandra, Det ger också den extra fördelen av automatiserad rensning av variabler i en funktion. Så snart funktionen avslutas kommer alla objekt som definierats i funktionen att rensas.

För att demonstrera, kopiera/klistra in funktionen nedan direkt i PowerShell-konsolen.

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

När du har klistrat in den, kör du funktionen. Lägg märke till att du inte kan komma åt $var-variabeln utanför funktionen.

PS> Do-ThingPS> $var

Att hålla objekt privata (inaktivera arv)

Typiskt sett, om en variabel definieras i ett överordnat scope, kommer den variabeln att definieras i det underordnade scope. Men du kanske vill använda ett variabelnamn men det variabelnamnet har redan definierats i ett av de scope som ska köras i en session. I det fallet kan du antingen välja ett annat variabelnamn eller definiera variabeln i ett privat scope vilket gör den till en privat variabel.

Ett sätt att använda scopes för att minska konflikter är att använda det privata scope. Genom att använda det privata scopeet inaktiverar du arv för den specifika variabeln. När flera underordnade scope skapas kommer dessa underordnade scope inte att se några variabler som definierats i ett privat scope.

Ett test.ps1-skript ger ut värdet på $a enligt nedan.

Write-Output $a

Du kan se nedan att jag definierar en variabel med privat scope i det globala scope och sedan kör test.ps1-skriptet. När du definierar en variabel i ett överordnat scope är variabeln vanligtvis tillgänglig i det underordnade scopet – så är inte fallet med en variabel med privat scope.

I exemplet nedan kan du se att det underordnade skriptomfånget som skapades genom att exekvera skriptet Test.ps1 inte kunde se den privata $a-variabeln $a som definierats i det överordnade området.

Skriptomfång kan inte se den privatskopierade variabeln

I motsats till globala scope eller AllScope-alternativet på cmdlet Set-Variable är privatskopierade variabler ett utmärkt sätt att dela upp objekt i fack.

Best Practices för scoping

Det är vanligt att man tror att det är bäst att definiera variabler i det globala omfånget eller använda alternativet AllScope. Alla variabler är trots allt tillgängliga överallt. Det finns ingen anledning att oroa sig för komplikationerna med scopes. Även om detta ger ytterligare frihet för åtkomst till det som definieras kan det snabbt gå överstyr och bli svårt att felsöka.

Istället för att försöka förhindra att använda scopes följer du dessa tips:

  1. Istället för att specificera scopes i funktioner använder du parametrar för att lämna över den information som krävs till funktionen.
  2. Håll dig inom det lokala scopeet så mycket som möjligt.
  3. Istället för att definiera globala variabler från ett skript använder du cmdlet Write-Output för att skriva ut allt och spara det till en variabel när det behövs från konsolen.

Den viktigaste poängen här är att omfamna scope och lära sig att använda dem till din fördel istället för att försöka kringgå dem.

Vidare läsning

  • Om avancerade funktioner och funktionsparametrar
  • Användning av miljövariabler i PowerShell

.

Similar Posts

Lämna ett svar

Din e-postadress kommer inte publiceras.