sobre escopos no Powershell
Estou aprendendo sobre escopos no Powershell e tenho algumas dúvidas:
- Sobre o "escopo local": Pelo que li, o escopo local é sempre o escopo atual. Então, por padrão, quando criamos um item (sem modificador de escopo), por exemplo, uma variável, em algum escopo, seja script ou global, o escopo será script/global de acordo. Portanto, minha pergunta é: quando precisaremos especificar explicitamente o
localmodificador? - MSDN disse:
Você pode criar um novo escopo executando um script ou função, criando uma sessão ou iniciando uma nova instância do PowerShell. Quando você cria um novo escopo, o resultado é um escopo pai (o escopo original) e um escopo filho (o escopo que você criou). ...
A menos que você explicitamente torne os itens privados, os itens no escopo pai estarão disponíveis para o escopo filho. No entanto, os itens que você cria e altera no escopo filho não afetam o escopo pai, a menos que você especifique explicitamente o escopo ao criar os itens.
Mas quando tento o seguinte:
PS> $Name = "John"
PS> Powershell.exe
PS>echo $Name // No Output
Parece da citação acima que "iniciar uma nova instância do powershell" é um escopo filho, portanto, todos os itens no escopo pai devem estar visíveis lá. Alguém pode explicar?
Respostas
Para complementar a resposta útil de iRon :
- [...] quando precisaremos especificar explicitamente o modificador local?
$local:raramente é necessário , porque o escopo local está implícito na ausência de um especificador de escopo.
No entanto, isso só se aplica se a variável referenciada realmente existir como uma variável local , uma vez que o escopo dinâmico do PowerShell torna variáveis de escopos ancestrais (pais) visíveis para escopos descendentes (filhos) também (consulte esta resposta para obter mais informações):
Por exemplo, digamos que você tenha
$foo = 'bar'declarado no escopo global , então a referência$fooem um script procuraria primeiro uma instância local ;$foose não houver nenhum, será usado um$foodefinido em um escopo ancestral (pai), se houver, que seria o global$fooneste exemplo e'bar'seria retornado.Por outro lado, se, em seu script, você usar
$local:foo, sem que uma$foovariável local seja definida, você obtém$nullpor padrão ou, seSet-StrictMode -Version 2ou superior estiver em vigor, ocorrerá um erro de finalização de instrução .
- MSDN diz: [...] criando uma sessão, ou iniciando uma nova instância do PowerShell [...] o resultado é um escopo pai (o escopo original) e um escopo filho (o escopo que você criou).
A documentação está incorreta a esse respeito no momento em que este livro foi escrito (um problema do GitHub foi registrado):
Relacionamentos ancestrais (pai-filho) entre escopos existem apenas no contexto de uma determinada sessão (runspace) .
Ou seja, o escopo dinâmico - a visibilidade de variáveis e outras definições de escopos ancestrais - só se aplica a escopos dentro de uma determinada sessão.
Uma exceção notável é que as funções de um módulo não são executadas em um escopo filho do escopo de chamada - exceto se o escopo de chamada for o escopo global ; os módulos têm seus próprios domínios de escopo (tecnicamente chamados de estados de sessão ) que estão vinculados apenas ao escopo global - consulte este problema de documentação do GitHub para uma discussão.
Portanto, nenhum escopo filho do escopo de chamada é criado nos seguintes cenários, onde o código recém-lançado não sabe nada sobre as variáveis (e outras definições) no escopo de chamada :
Iniciar uma nova sessão via comunicação remota do PowerShell (por exemplo, com Enter-PSSession) ouInvoke-Command -Computer
Iniciando um trabalho [thread] em segundo plano com Start-Jobou Start-ThreadJobexecutando threads em paralelo com ForEach-Object -Parallela versão 7.0+
Iniciando uma nova instância do PowerShell (processo) , usando a CLI do PowerShell (
pwshpara PowerShell [Core],powershell.exepara Windows PowerShell).Para comunicar valores do escopo de chamada para o código recém-lançado nesses cenários, é necessária uma ação explícita :
- Ao chamar a CLI ou usar , onde um processo
Start-Jobfilho na mesma máquina é criado, apenas as variáveis de ambiente definidas no processo de chamada ficam automaticamente disponíveis para o processo filho. - Caso contrário, os valores do chamador devem ser passados como argumentos ou - exceto ao usar a CLI - por meio do
$using:escopo - veja esta resposta .
- Ao chamar a CLI ou usar , onde um processo
Começando com a última pergunta:
Os escopos entram em jogo com funções e scripts invocados (cmdlets), como:
Function Test {
$Test++
Write-Host 'Local:' $Test
}
$Test = 5
Test
Write-Host 'Global:' $Test
Retorna:
Local: 6
Global: 5
E:
Function Test {
$Global:Test++
Write-Host 'Local:' $Test
}
$Test = 5
Test
Write-Host 'Global:' $Test
Retorna:
Local: 6
Global: 6
Ou se você colocar a função em um script (por exemplo MyScript.ps1):
$Test = 5
.\MyScript.ps1
Write-Host $Test # $Test is unaffected unless you use the $Global scope in your script
Que retornará basicamente os mesmos resultados acima, a menos que você Dot-Source seu script onde ele será executado no escopo atual:
$Test = 5
. .\MyScript.ps1
Write-Host $Test # $Test might be affected by MyScript.ps1 if you just use $Test
Para o que você está fazendo:
Você está criando uma nova sessão completa do PowerShell (com Powershell.exe) que começará com uma nova lista de variáveis.
Observe aqui que você verá as variáveis iniciais novamente se exitda nova sessão:
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
Com relação à primeira pergunta, não acho que existam muitos aplicativos em que você precise se referir explicitamente ao $Localescopo, mas para dar um exemplo de onde você pode usá-lo:
$Test = 5
Function Test {
Write-Host ($Local:Test++)
}
Test
No exemplo acima, o operador de incremento unário começará com 0se você usar explicitamente o $Localescopo (na verdade, você começa com uma variável local vazia que será convertida para 0) e com 5se você omitir o $Localescopo onde herdará uma cópia da $Testvariável de o escopo pai.