sobre alcances en Powershell
Estoy aprendiendo sobre alcances en Powershell y tengo algunas preguntas:
- Acerca del "ámbito local": por lo que leí, el ámbito local es siempre el ámbito actual. Entonces, por defecto, cuando creamos un elemento (sin modificador de alcance), por ejemplo, una variable, en algún alcance, ya sea script o global, el alcance será script/global en consecuencia. Entonces mi pregunta es: ¿cuándo necesitaremos especificar explícitamente el
localmodificador? - MSDN dice:
Puede crear un nuevo ámbito ejecutando un script o una función, creando una sesión o iniciando una nueva instancia de PowerShell. Cuando crea un nuevo ámbito, el resultado es un ámbito primario (el ámbito original) y un ámbito secundario (el ámbito que creó). ...
A menos que haga que los elementos sean privados de forma explícita, los elementos del ámbito principal están disponibles para el ámbito secundario. Sin embargo, los elementos que crea y modifica en el ámbito secundario no afectan al ámbito principal, a menos que especifique explícitamente el ámbito al crear los elementos.
Pero cuando intento lo siguiente:
PS> $Name = "John"
PS> Powershell.exe
PS>echo $Name // No Output
De la cita anterior parece que "iniciar una nueva instancia de powershell" es un ámbito secundario, por lo que todos los elementos en el ámbito principal deberían estar visibles allí. ¿Alguien puede explicar?
Respuestas
Para complementar la útil respuesta de iRon :
- [...] ¿cuándo necesitaremos especificar explícitamente el modificador local?
$local:rara vez se requiere , porque el alcance local está implícito en ausencia de un especificador de alcance.
Sin embargo, eso solo se aplica si la variable a la que se hace referencia realmente existe como una variable local , dado que el ámbito dinámico de PowerShell hace que las variables de los ámbitos ancestrales (principales) sean visibles para los ámbitos descendientes (secundarios) también (consulte esta respuesta para obtener más información):
Por ejemplo, supongamos que ha
$foo = 'bar'declarado en el ámbito global , luego hacer referencia$fooen un script buscaría primero una instancia local ;$foosi no hay ninguno, se usa un$foodefinido en un ámbito ancestral (principal), si lo hay, que sería el global$fooen este ejemplo, y'bar'se devolvería.Por el contrario, si, en su secuencia de comandos, usa
$local:foo, sin$fooque se haya definido una variable local, obtiene$nullpor defecto o, siSet-StrictMode -Version 2o superior está en efecto, se produce un error de terminación de declaración .
- MSDN dice: [...] al crear una sesión o al iniciar una nueva instancia de PowerShell [...] el resultado es un ámbito principal (el ámbito original) y un ámbito secundario (el ámbito que creó).
La documentación es incorrecta en este sentido a partir de este escrito ( se ha presentado un problema de GitHub ):
Las relaciones ancestrales (principal-secundario) entre ámbitos solo existen en el contexto de una sesión determinada (espacio de ejecución) .
Es decir, el alcance dinámico (la visibilidad de las variables y otras definiciones de los alcances ancestrales) solo se aplica a los alcances dentro de una sesión determinada.
Una excepción notable es que las funciones de un módulo no se ejecutan en un ámbito secundario del ámbito de llamada, excepto si el ámbito de llamada es el ámbito global ; los módulos tienen sus propios dominios de alcance (técnicamente llamados estados de sesión ) que están vinculados solo al alcance global; consulte este problema de documentos de GitHub para una discusión.
Por lo tanto, no se crea ningún ámbito secundario del ámbito de llamada en los siguientes escenarios, donde el código recién lanzado no sabe nada de las variables (y otras definiciones) en el ámbito de llamada :
Iniciar una nueva sesión a través de la comunicación remota de PowerShell (p. ej., con Enter-PSSession) oInvoke-Command -Computer
Comenzar un trabajo [subproceso] en segundo plano con Start-Jobo Start-ThreadJobejecutando subprocesos en paralelo con ForEach-Object -Parallelv7.0+
Inicio de una nueva instancia de PowerShell (proceso) mediante la CLI de PowerShell (
pwshpara PowerShell [Core],powershell.exepara Windows PowerShell).Para comunicar valores desde el alcance de la llamada al código recién lanzado en estos escenarios, se requiere una acción explícita :
- Al llamar a la CLI o usar
Start-Job, donde se crea un proceso secundario en la misma máquina, solo las variables de entorno definidas en el proceso de llamada estarán disponibles automáticamente para el proceso secundario. - De lo contrario, los valores de la persona que llama se deben pasar como argumentos o, excepto cuando se usa la CLI, a través del
$using:alcance, consulte esta respuesta .
- Al llamar a la CLI o usar
Empezando por la última pregunta:
Los ámbitos entran en juego con funciones y scripts invocados (cmdlets), como:
Function Test {
$Test++
Write-Host 'Local:' $Test
}
$Test = 5
Test
Write-Host 'Global:' $Test
Devoluciones:
Local: 6
Global: 5
Y:
Function Test {
$Global:Test++
Write-Host 'Local:' $Test
}
$Test = 5
Test
Write-Host 'Global:' $Test
Devoluciones:
Local: 6
Global: 6
O si pones la función en un script (por ejemplo MyScript.ps1):
$Test = 5
.\MyScript.ps1
Write-Host $Test # $Test is unaffected unless you use the $Global scope in your script
Lo que devolverá básicamente los mismos resultados que el anterior, a menos que haga un punto de origen de su secuencia de comandos donde se ejecutará en el ámbito actual:
$Test = 5
. .\MyScript.ps1
Write-Host $Test # $Test might be affected by MyScript.ps1 if you just use $Test
Para lo que está haciendo:
está creando una nueva sesión de PowerShell completa (con Powershell.exe) que comenzará con una nueva lista de variables.
Tenga en cuenta aquí que volverá a ver las variables iniciales si viene exitde la nueva sesión:
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
Con respecto a la primera pregunta, no creo que haya muchas aplicaciones en las que deba referirse explícitamente al $Localalcance, pero para darle un ejemplo donde podría usarlo:
$Test = 5
Function Test {
Write-Host ($Local:Test++)
}
Test
En el ejemplo anterior, el operador de incremento unario comenzará 0si usa explícitamente el $Localalcance (de hecho, comienza con una variable local vacía que se convertirá en 0) y 5si omite el $Localalcance donde heredará una copia de la $Testvariable de el ámbito principal.