Adam the Automator

author
12 minutes, 56 seconds Read

Quando si scrive uno script PowerShell senza funzioni e senza dipendenze esterne da altri script, il concetto di ambiti PowerShell non è molto importante. Il concetto di variabili globali PowerShell non è di primo piano. Ma quando cominci a costruire funzioni, moduli e impari a chiamare script da altri script, l’argomento diventa più importante.

In questo articolo, riceverai una profonda lezione su cosa sono gli scope in PowerShell, come funzionano, e come puoi scrivere codice con gli scope in mente. Quando avrai finito, capirai le variabili globali di PowerShell e molto di più!

Tabella dei contenuti

Scopi: Kinda Like Buckets

Hai mai scritto uno script dove definisci una variabile e quando controlli il valore di quella variabile, è qualcos’altro? Potreste grattarvi la testa su come quella variabile sia cambiata quando l’avete chiaramente definita. Una ragione potrebbe essere che il valore della variabile viene sovrascritto in un altro ambito.

Forse vi siete chiesti come certe variabili di PowerShell abbiano valori quando fate riferimento ad esse nella vostra console, ma non esistono nei vostri script. È probabile che quelle variabili siano in un altro ‘bucket’ che non è disponibile in quel momento.

Gli scope sono come i bucket. Gli scope influenzano il modo in cui PowerShell isola variabili, alias, funzioni e PSDrive tra aree diverse. Uno scope è come un secchio. È un posto per raccogliere tutti questi elementi insieme.

Quando PowerShell inizia, crea automaticamente questi “secchi” per te. A quel punto, stai già usando gli scope senza rendertene conto. Tutti gli ambiti sono definiti da PowerShell e vengono creati senza alcun aiuto da parte tua.

Tipi di ambito

Quando PowerShell si avvia, crea automaticamente quattro “secchi” o ambiti per i vari elementi da inserire. Non puoi creare scope da solo. Puoi solo aggiungere e rimuovere elementi da questi ambiti definiti di seguito.

Ambito globale

Gli elementi definiti quando PowerShell si apre sono impostati nell’ambito globale. Questi elementi includono oggetti creati dal sistema come le unità PowerShell e anche tutto ciò che hai definito in un profilo PowerShell poiché il tuo profilo viene eseguito all’avvio.

Gli elementi nell’ambito globale sono disponibili ovunque. Puoi fare riferimento agli elementi nell’ambito globale in modo interattivo sulla console, in qualsiasi script che esegui e in qualsiasi funzione. Le variabili globali di PowerShell sono ovunque. Per questo motivo, l’uso comune delle variabili globali di PowerShell è quello di utilizzare le variabili globali di PowerShell tra gli script.

C’è solo un ambito globale che governa nel complesso.

Script Scope

Uno script scope viene creato automaticamente ogni volta che viene eseguito uno script PowerShell. Potete avere molte istanze diverse di script scope. Gli scope di script vengono creati quando esegui uno script PS1 o un modulo, per esempio.

Solo gli elementi creati in quella particolare istanza di scope di script possono fare riferimento l’uno all’altro.

Scope privato

In genere, un elemento definito può essere accessibile da altri scope – non è vero con gli elementi in uno scope privato. Gli elementi in uno scope privato contengono oggetti che sono nascosti agli altri scope. Uno scope privato è usato per creare oggetti con lo stesso nome di oggetti in altri scope per evitare sovrapposizioni.

Scopo locale

A differenza degli altri scope, lo scope locale è un po’ diverso. L’ambito locale è un puntatore all’ambito globale, script o privato. Lo scope locale è relativo a qualsiasi contesto in cui il codice viene eseguito in quel momento.

Se create una variabile, un alias, una funzione o un PSDrive senza assegnargli esplicitamente uno scope (di cui ci occuperemo più avanti) andrà nello scope locale.

Fare riferimento agli scope

Ora che hai un’idea dei quattro tipi di scope che esistono, dovresti anche sapere che ci sono due modi per fare riferimento a questi scope.

In PowerShell, ci sono due modi per fare riferimento agli scope – con nome o numerati. Entrambi i metodi fanno riferimento agli stessi ambiti, ma semplicemente in un modo diverso. Questi sono due modi diversi di interagire con gli scope.

Named Scopes

Sopra, nella sezione Scope Type, hai imparato gli scope referenziati per nome. Riferirsi a uno scope per nome è, intuitivamente, chiamato named scope. Lo scopo principale di referenziare uno scope per nome è quello di assegnare un elemento ad uno scope. Imparerai come fare questo di seguito.

Scope numerati

Oltre ad un nome, ogni scope ha un numero che inizia da zero e che sarà sempre lo scope locale. Gli scope sono numerati dinamicamente in relazione allo scope locale corrente.

Per esempio, appena apri una sessione PowerShell stai operando nello scope globale. A questo punto, lo scope globale è lo scope locale (ricordate che lo scope locale è solo un puntatore).

Siccome lo scope locale è sempre lo scope zero, a questo punto, lo scope globale è anche attualmente lo scope zero. Ma, quando si esegue uno script da quella stessa sessione, viene creato uno scope dello script. Durante l’esecuzione, il puntatore dello scope locale è quindi cambiato nello scope dello script. Ora lo scope dello script è lo scope zero e lo scope globale è lo scope uno.

Gli scope sono numerati Questo processo si ripete per tutti gli scope che avete dove lo scope locale è 0. Gli scope sono numerati dinamicamente dalla gerarchia degli scope.

Gerarchia ed ereditarietà degli scope

Come detto prima, quando lanci una sessione PowerShell, PowerShell crea alcuni elementi per te nello scope globale. Questi elementi possono essere funzioni, variabili, alias o PSDrive. Qualsiasi cosa tu definisca nella tua sessione PowerShell sarà definita anche nell’ambito globale.

Siccome sei nell’ambito globale di default, se fai qualcosa che crea un altro ambito come l’esecuzione di uno script o l’esecuzione di una funzione, verrà creato un ambito figlio con il genitore che è l’ambito globale. Gli scope sono come processi con genitori e figli.

Tutto ciò che è definito in uno scope padre, lo scope globale, in questo caso, sarà accessibile nello scope figlio. Ma questi elementi sono modificabili solo nell’ambito in cui sono stati definiti.

Diciamo che avete uno script chiamato Test.ps1. All’interno di questo script c’è una singola linea come mostrato qui sotto.

$a = 'Hello world!'

Quando eseguite questo script, a $a viene assegnato un valore nell’ambito locale (script mentre lo script è in esecuzione). Quando Test.ps1 viene eseguito, potete vedere qui sotto che non siete in grado di fare riferimento ad esso dopo l’esecuzione dello script. Poiché $a è stato assegnato mentre si trova nell’ambito dello script, l’ambito globale (alla console interattiva) non può vederlo.

L’ambito globale non può vedere la variabile

Facciamo un ulteriore passo avanti in questo esempio e rendiamo lo script Test.ps1 simile a quello che segue. Lo script sta ora tentando di emettere il valore di $a prima di impostarlo nello stesso scope.

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

Per dimostrare, assegnate un valore a $a alla console interattiva. Questo assegna il valore all’ambito globale. Ora, lo script viene eseguito, erediterà l’ambito genitore (globale) e dovrebbe essere in grado di vedere il valore.

Si può vedere sotto che quando Test.ps1 viene eseguito (creando un ambito figlio dell’ambito globale), può vedere il valore di $a. Potete anche vedere che il valore della variabile è disponibile anche nell’ambito globale poiché questo ambito è quello in cui è stato impostato. Questo significa che $a è disponibile sia nello scope dello script (figlio) che in quello del genitore (globale).

La variabile è disponibile negli scope dello script e in quello globale

Ricorda questo comportamento dell’ereditarietà degli scope. Così facendo vi aiuterà nella risoluzione dei conflitti tra variabili, come le variabili in scope diversi con lo stesso nome.

Definire e accedere agli elementi negli scope

Ora che sapete cos’è uno scope e come funziona, come vi si accede? Vediamo come usare PowerShell per impostare uno scope di variabile (e accedervi).

Get/Set-Variable

In PowerShell, ci sono due cmdlets che ti permettono di impostare variabili chiamate Get-Variable e Set-Variable. Queste cmdlets ti permettono di recuperare il valore di una variabile o di definire un valore.

Entrambe le cmdlets sono simili con un parametro Name e Scope. Usando questi parametri, PowerShell ti permette di impostare e recuperare i valori delle variabili in tutti gli ambiti.

Ambiti locali

Per impostare una variabile in un ambito locale, usa Set-Variable e fornisci un nome di variabile locale e un valore come mostrato sotto.

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

L’ambito locale è sempre quello predefinito, quindi non usando il parametro Scope la variabile verrà sempre definita nell’ambito locale.

Per recuperare il valore di una variabile con scope locale, usate Get-Variable fornendogli il nome.

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

Scope privati/Script/Globali

Utilizzerete gli stessi parametri (Name e Value) anche quando lavorate con variabili private, script e globali. L’unica differenza è che questa volta userete il parametro Scope per definire esplicitamente lo scopo.

I metodi per impostare una variabile privata, script o globale sono gli stessi. Sostituite semplicemente il valore passato al parametro Scope come Private, Script Global.

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

Per recuperare il valore di una variabile script o global-scope, usate Get-Variable fornendogli il nome e lo scope.

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

Nota: Puoi anche fare riferimento agli scope usando i numeri degli scope invece dei nomi con le cmdlets Get/Set-Variable.

Scope “Prefacing”

Puoi anche recuperare e impostare variabili negli scope usando una scorciatoia. Invece di usare le cmdlets di PowerShell, preferirete l’ambito alla variabile quando la referenziate.

Scopi locali

Siccome l’ambito locale è sempre quello predefinito, definendo semplicemente una variabile e facendo riferimento ad essa imposterai e recupererai una variabile dell’ambito locale

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

Scope privati/Script/Globali

Se vuoi definire e fare riferimento a scope di script o globali, puoi preferire le variabili con il nome dell’ambito e un punto e virgola.

Per esempio, per impostare la variabile $a nell’ambito globale, potete anteporre a con $global:.

$global:a = 'foo'

Lo stesso può essere fatto per una variabile con ambito script.

$script:a = 'foo'

Una volta che le variabili sono impostate nell’ambito preferito, le referenzierete allo stesso modo. Inoltre, notate che potete escludere la prefazione dello scope se lo scope definito è lo scope locale.

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

Scopes in Scriptblocks

PowerShell ha un comodo costrutto chiamato scriptblocks. Gli scriptblock ti permettono di spostare frammenti di codice ed eseguirli praticamente ovunque.

Come uno script PS1, gli scriptblock vengono eseguiti nel proprio ambito di script. Quando eseguite uno scriptblock, state essenzialmente eseguendo uno script PS1.

Nota nell’esempio qui sotto dove una variabile è definita nell’ambito globale e poi tentata di sovrascrivere in un ambito di script. Come avete imparato sopra, questo non funzionerà perché un ambito figlio non può fare riferimento ad un ambito genitore.

Un ambito figlio non può sovrascrivere un ambito genitore

Questo esempio mostra che quando $a viene cambiato nello scriptblock, la definizione della variabile globale per $a non viene modificata perché lo scriptblock è un ambito figlio dello script.

Script Dot Sourcing (scambio di scope locali)

PowerShell ha un concetto chiamato dot-sourcing. Questo è un metodo che ti permette di eseguire uno script PS1 e portare tutto ciò che sarebbe script-scoped nello scope locale.

Mettendo un punto (.) prima di fare riferimento a uno script PS1 ed eseguirlo, questo “dot-sourcing” il contenuto dello script e porta tutto nello scope locale.

Per dimostrarlo, ho di nuovo uno script Test.ps1 che definisce una variabile come mostrato di seguito.

$a = 'Hello world!'

In una console PowerShell, impostate un valore per una variabile $a e poi dot source questo script come mostrato di seguito. Notate che la variabile originale è stata sovrascritta. PowerShell ha “fuso” i due scope locali insieme.

Variabile originale sovrascritta

Utilizzo della proprietà AllScope (opzione)

Hai visto come interagire con elementi in scope specifici, ma finora ogni elemento è ancora definito in un singolo scope. Ma cosa succede se non sai in quale ambito è definita una variabile?

Quando definisci una variabile con la cmdlet Set-Variable, puoi mettere la variabile in tutti gli ambiti contemporaneamente. Per farlo, usate il valore AllScope per il parametro Option.

Per dimostrarlo, lo script Test.ps1 è stato modificato per impostare una variabile in tutti gli scopi. Questa variabile viene poi emessa come mostrato di seguito.

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

Si può quindi vedere di seguito che viene impostato un valore per $a nell’ambito globale e lo script Test.ps1 viene eseguito. Tuttavia, invece di non avere alcun effetto, il valore di $a è stato sovrascritto. Non solo è stato definito nell’ambito dello script (Write-Output $a) ma ha anche sovrascritto l’ambito globale.

Variabile sovrascritta nell’ambito globale

L’opzione AllScope è comoda ma fate attenzione. Questa opzione essenzialmente elimina il concetto di scope e fonde tutto insieme.

Scope di funzione

Quando esegui una funzione, tutto il codice all’interno di quella funzione è nel suo scope figlio. Gli scope di funzione seguono lo stesso comportamento figlio/genitore degli altri scope.

Avere scope separati per ogni funzione è una buona idea. Permette un migliore controllo sugli oggetti senza doversi preoccupare degli oggetti in conflitto, e dà anche l’ulteriore vantaggio di una pulizia automatica delle variabili in una funzione. Non appena la funzione viene completata, tutti gli elementi definiti nella funzione saranno cancellati.

Per dimostrarlo, copiate/incollate la funzione sottostante direttamente nella console di PowerShell.

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

Una volta incollata, eseguite la funzione. Notate che non potete accedere alla variabile $var al di fuori della funzione.

PS> Do-ThingPS> $var

Mantenere gli elementi privati (disabilitare l’ereditarietà)

In genere, se una variabile è definita in un ambito padre, quella variabile sarà definita nell’ambito figlio. Ma forse vorreste usare un nome di variabile, ma quel nome di variabile è già stato definito in uno degli scope da eseguire in una sessione. In questo caso, potete scegliere un nome di variabile diverso o definire la variabile in uno scope privato rendendola una variabile privata.

Un modo di usare gli scope per ridurre i conflitti è usare lo scope privato. Usare lo scope privato disabilita l’ereditarietà su quella specifica variabile. Quando vengono creati più scope figli, questi scope figli non vedranno nessuna variabile definita in uno scope privato.

Uno script Test.ps1 mostra il valore di $a come mostrato qui sotto.

Write-Output $a

Si può vedere che sto definendo una variabile con scope privato nello scope globale e poi eseguendo lo script Test.ps1. Di solito, quando si definisce una variabile in un ambito genitore, quella variabile sarà disponibile nell’ambito figlio – non è così con una variabile con ambito privato.

Nell’esempio seguente, si può vedere che l’ambito figlio dello script creato dall’esecuzione dello script Test.ps1 non può vedere la variabile $a d’ambito privato definita nell’ambito padre.

Lo scope dello script non può vedere la variabile private-scope

A differenza degli scope globali o dell’opzione AllScope della cmdlet Set-Variable, le variabili private-scope sono un modo eccellente per compartimentare gli elementi.

Migliori pratiche di scoping

Pensare che definire le variabili nell’ambito globale o usare l’opzione AllScope sia la strada da seguire è comune. Dopo tutto, tutte le variabili sono disponibili ovunque. Non c’è bisogno di preoccuparsi delle complicazioni degli scope. Mentre questo fornisce un’ulteriore libertà per l’accesso a ciò che è definito, può rapidamente sfuggire di mano e diventare difficile da risolvere.

Invece di cercare di prevenire l’uso degli scope seguite questi consigli:

  1. Invece di specificare gli scope nelle funzioni, usate i parametri per passare le informazioni richieste alla funzione.
  2. Stare all’interno dello scope locale il più possibile.
  3. Invece di definire variabili globali da uno script, usa il cmdlet Write-Output per mostrare tutto e salvarlo in una variabile quando necessario dalla console.

Il punto principale qui è di abbracciare gli scopi e imparare a usarli a tuo vantaggio invece di cercare di aggirarli.

Ulteriori letture

  • Sulle funzioni avanzate e i parametri di funzione
  • Usare le variabili di ambiente in PowerShell

Similar Posts

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.