sugli ambiti in Powershell

Aug 24 2020

Sto imparando a conoscere gli ambiti in Powershell e ho alcune domande:

  1. Informazioni sull '"ambito locale": da quello che ho letto, l'ambito locale è sempre l'ambito corrente. Quindi, per impostazione predefinita, quando creiamo un elemento (senza modificatore di ambito), ad esempio una variabile, in un ambito, lascia che sia script o globale, l'ambito sarà script/global di conseguenza. Quindi la mia domanda è: quando avremo bisogno di specificare esplicitamente il localmodificatore?
  2. MSDN dice:

Puoi creare un nuovo ambito eseguendo uno script o una funzione, creando una sessione o avviando una nuova istanza di PowerShell. Quando crei un nuovo ambito, il risultato è un ambito padre (l'ambito originale) e un ambito figlio (l'ambito che hai creato). ...
A meno che non si renda esplicitamente privati ​​gli elementi, gli elementi nell'ambito padre sono disponibili per l'ambito figlio. Tuttavia, gli elementi che crei e modifichi nell'ambito figlio non influiscono sull'ambito padre, a meno che tu non specifichi esplicitamente l'ambito quando crei gli elementi.

Ma quando provo quanto segue:

PS> $Name = "John"
PS> Powershell.exe
PS>echo $Name  // No Output

Dalla citazione sopra sembra che "l'avvio di una nuova istanza di PowerShell" sia un ambito figlio, quindi tutti gli elementi nell'ambito padre dovrebbero essere visibili lì. Qualcuno può spiegare?

Risposte

2 mklement0 Aug 24 2020 at 23:16

Per completare l'utile risposta di iRon :

  1. [...] quando sarà necessario specificare esplicitamente il modificatore locale?

$local:è raramente richiesto perché l'ambito locale è implicito in assenza di un identificatore di ambito.

Tuttavia, ciò si applica solo se la variabile di riferimento esiste effettivamente come local variable , dato che l' ambito dinamico di PowerShell rende visibili anche le variabili dagli ambiti ancestrali (padre) agli ambiti discendenti (figlio) (vedere questa risposta per ulteriori informazioni):

  • Ad esempio, supponiamo di aver $foo = 'bar'dichiarato nell'ambito globale , quindi fare riferimento a $fooin uno script cercherebbe prima un'istanza locale ; $foose non ce n'è, viene utilizzato un ambito $foodefinito in un ambito ancestrale (padre), se presente, che sarebbe il globale $foo in questo esempio e 'bar'verrebbe restituito.

  • Al contrario, se, nel tuo script, usi $local:foo, senza $fooche sia definita una variabile locale, ottieni $nullper impostazione predefinita o, se Set-StrictMode -Version 2o superiore è in vigore, si verifica un errore di terminazione dell'istruzione .


  1. MSDN dice: [...] creando una sessione o avviando una nuova istanza di PowerShell [...] il risultato è un ambito padre (l'ambito originale) e un ambito figlio (l'ambito che hai creato).

La documentazione non è corretta a questo proposito al momento della stesura di questo documento ( è stato archiviato un problema di GitHub ):

  • Le relazioni ancestrali (padre-figlio) tra gli ambiti esistono solo nel contesto di una determinata sessione (spazio di esecuzione) .

    • Ovvero, l'ambito dinamico - la visibilità di variabili e altre definizioni da ambiti ancestrali - si applica solo agli ambiti all'interno di una determinata sessione.

    • Un'eccezione degna di nota è che le funzioni di un modulo non vengono eseguite in un ambito figlio dell'ambito chiamante, a meno che l'ambito chiamante non sia l' ambito globale ; i moduli hanno i propri domini di ambito (tecnicamente chiamati stati di sessione ) che sono collegati solo all'ambito globale: vedere questo problema di documentazione di GitHub per una discussione.

  • Pertanto, non viene creato alcun ambito figlio dell'ambito chiamante nei seguenti scenari, in cui il codice appena avviato non conosce nulla delle variabili (e altre definizioni) nell'ambito chiamante :

    • Avvio di una nuova sessione tramite la comunicazione remota di PowerShell (ad esempio, con Enter-PSSession) oInvoke-Command -Computer

    • Avvio di un processo [thread] in background con Start-Jobo Start-ThreadJobo esecuzione di thread in parallelo con ForEach-Object -Parallelin v7.0+

    • Avvio di una nuova istanza (processo) di PowerShell , utilizzando l' interfaccia della riga di comando di PowerShell ( pwshper PowerShell [Core], powershell.exeper Windows PowerShell).

    • Per comunicare i valori dall'ambito chiamante al codice appena avviato in questi scenari, è richiesta un'azione esplicita :

      • Quando si chiama la CLI o si utilizza , in cui viene creato un processoStart-Job figlio sulla stessa macchina, solo le variabili di ambiente definite nel processo chiamante diventano automaticamente disponibili per il processo figlio.
      • In caso contrario, i valori del chiamante devono essere passati come argomenti o, tranne quando si utilizza la CLI, tramite l' $using:ambito, vedere this answer .
4 iRon Aug 24 2020 at 14:48

A partire da quest'ultima domanda:

Gli ambiti entrano in gioco con funzioni e script richiamati (cmdlet), come:

Function Test {
    $Test++
    Write-Host 'Local:' $Test
}
$Test = 5
Test
Write-Host 'Global:' $Test

Ritorna:

Local: 6
Global: 5

E:

Function Test {
    $Global:Test++
    Write-Host 'Local:' $Test
}
$Test = 5
Test
Write-Host 'Global:' $Test

Ritorna:

Local: 6
Global: 6

O se metti la funzione in uno script (es MyScript.ps1):

$Test = 5
.\MyScript.ps1
Write-Host $Test # $Test is unaffected unless you use the $Global scope in your script

Che restituirà fondamentalmente gli stessi risultati di cui sopra, a meno che non Dot-Source il tuo script dove verrà eseguito nell'ambito corrente:

$Test = 5
. .\MyScript.ps1
Write-Host $Test # $Test might be affected by MyScript.ps1 if you just use $Test

Per quello che stai facendo:
stai creando una nuova sessione PowerShell completa (con Powershell.exe) che inizierà con un nuovo elenco di variabili.
Nota qui che vedrai di nuovo le variabili iniziali se exitdalla nuova sessione:

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

Per quanto riguarda la prima domanda, non penso che ci siano molte applicazioni in cui è necessario fare esplicito riferimento $Localall'ambito, ma per darti un esempio in cui potresti usarlo:

$Test = 5
Function Test {
    Write-Host ($Local:Test++)
}
Test

Nell'esempio sopra l' operatore di incremento unario inizierà con 0se usi esplicitamente l' $Localambito (infatti inizi con una variabile locale vuota che verrà convertita in 0) e con 5se ometti l' $Localambito in cui erediterai una copia della $Testvariabile da l'ambito principale.