sugli ambiti in Powershell
Sto imparando a conoscere gli ambiti in Powershell e ho alcune domande:
- 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
local
modificatore? - 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
Per completare l'utile risposta di iRon :
- [...] 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$foo
in uno script cercherebbe prima un'istanza locale ;$foo
se non ce n'è, viene utilizzato un ambito$foo
definito 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$foo
che sia definita una variabile locale, ottieni$null
per impostazione predefinita o, seSet-StrictMode -Version 2
o superiore è in vigore, si verifica un errore di terminazione dell'istruzione .
- 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 (
pwsh
per PowerShell [Core],powershell.exe
per 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 processo
Start-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 .
- Quando si chiama la CLI o si utilizza , in cui viene creato un processo
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 exit
dalla 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 $Local
all'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 0
se usi esplicitamente l' $Local
ambito (infatti inizi con una variabile locale vuota che verrà convertita in 0
) e con 5
se ometti l' $Local
ambito in cui erediterai una copia della $Test
variabile da l'ambito principale.