Ámbitos de PowerShell: Comprendiendo el Ámbito de Variables

Cuando escribes un script de PowerShell sin funciones y sin dependencias externas de otros scripts, el concepto de ámbitos de PowerShell no es de mucha preocupación. El concepto de variables globales de PowerShell no está en primer plano. Pero a medida que empiezas a construir funciones, módulos y aprendes a llamar scripts desde otros scripts, el tema se vuelve más importante.

En este artículo, vas a recibir una lección profunda sobre qué son los ámbitos en PowerShell, cómo funcionan y cómo puedes escribir código teniendo en cuenta los ámbitos. Cuando termines, entenderás las variables globales de PowerShell y mucho más.

Ámbitos: algo así como cubetas

¿Alguna vez has escrito un script donde defines una variable y cuando verificas el valor de esa variable, es algo diferente? Puede que te rasques la cabeza preguntándote cómo esa variable cambió cuando claramente la definiste. Una razón puede ser que el valor de la variable se esté sobrescribiendo en otro ámbito.

Tal vez te hayas preguntado cómo ciertas variables de PowerShell tienen valores cuando las referencias en tu consola pero no existen en tus scripts. Es probable que esas variables estén en otra “cubeta” que no está disponible en ese momento.

Los ámbitos son como cubetas. Los ámbitos afectan la forma en que PowerShell aísla variables, alias, funciones y PSDrives entre diferentes áreas. Un ámbito es como una cubeta. Es un lugar para recopilar todos estos elementos juntos.

Cuando PowerShell se inicia, crea automáticamente estas “cubetas” para ti. En ese momento, ya estás utilizando ámbitos sin darte cuenta. Todos los ámbitos son definidos por PowerShell y se crean sin ninguna ayuda de tu parte.

Tipos de alcance

Cuando PowerShell se inicia, crea automáticamente cuatro “contenedores” o alcances para colocar diversos elementos. No se pueden crear alcances por cuenta propia. Solo se pueden agregar y eliminar elementos de los alcances definidos a continuación.

Alcance global

Los elementos definidos al abrir PowerShell se establecen en el alcance global. Estos elementos incluyen objetos creados por el sistema, como las unidades de PowerShell, y también cualquier cosa que hayas definido en un perfil de PowerShell ya que tu perfil se ejecuta al inicio.

Los elementos en el alcance global están disponibles en todas partes. Puedes hacer referencia a los elementos en el alcance global de forma interactiva en la consola, en cualquier script que ejecutes y en cualquier función. Las variables globales de PowerShell están en todas partes. Por esta razón, el uso común de las variables globales de PowerShell es utilizarlas entre scripts.

Existe solo un alcance global que tiene el control absoluto.

Alcance de script

A script scope is automatically created every time a PowerShell script runs. You can have many different script scope instances. Script scopes are created when you execute a PS1 script or a module, for example.

Solo los elementos creados en esa instancia de alcance de script particular pueden hacer referencia entre sí.

Alcance privado

Normalmente, un elemento definido puede ser accedido desde otros alcances, pero esto no es cierto con los elementos en un alcance privado. Los elementos en un alcance privado contienen objetos que están ocultos para otros alcances. Se utiliza un alcance privado para crear elementos con el mismo nombre que los elementos en otros alcances para evitar la superposición.

Alcance local

A diferencia de los otros alcances, el alcance local es un poco diferente. El alcance local es un puntero al alcance global, de script o privado. El alcance local es relativo al contexto en el que se ejecuta el código en ese momento.

Si creas una variable, alias, función o PSDrive sin asignarle explícitamente un alcance (lo cual veremos más adelante), se colocará en el alcance local.

Referencia a los alcances

Ahora que tienes una idea de los cuatro tipos de alcances que existen, también debes saber que hay dos formas de referenciar esos alcances.

En PowerShell, hay dos formas de referenciar alcances: por nombre o por número. Ambos métodos hacen referencia a los mismos alcances, pero simplemente de una manera diferente. Estas son dos formas diferentes de interactuar con los alcances.

Alcances por nombre

Arriba, en la sección de Tipos de alcances, aprendiste sobre los alcances referenciados por nombre. Hacer referencia a un alcance por nombre se llama, intuitivamente, un alcance por nombre. El propósito principal de hacer referencia a un alcance por nombre es asignar un elemento a un alcance. Aprenderás cómo hacerlo a continuación.

Alcances numerados

Junto con un nombre, cada alcance tiene un número que comienza en cero, que siempre será el alcance local. Los alcances se numeran dinámicamente en relación con el alcance local actual.

Por ejemplo, tan pronto como abras una sesión de PowerShell, estarás operando en el alcance global. En este punto, el alcance global es el alcance local (recuerda que el alcance local es solo un puntero).

Dado que el ámbito local siempre es el ámbito cero, en este momento, el ámbito global también es actualmente el ámbito cero. Sin embargo, cuando ejecutas un script desde esa misma sesión, se crea un ámbito de script. Al ejecutarlo, el puntero del ámbito local ha cambiado al ámbito de script. Ahora el ámbito de script es el ámbito cero y el ámbito global es el ámbito uno.

Los ámbitos se numeran. Este proceso se repite tantas veces como ámbitos tengas donde el ámbito local sea 0. Los ámbitos se numeran dinámicamente según la jerarquía de ámbitos.

Jerarquía de ámbitos y herencia

Como se mencionó anteriormente, cuando inicias una sesión de PowerShell, PowerShell crea algunos elementos para ti en el ámbito global. Estos elementos pueden ser funciones, variables, aliases o PSDrives. Todo lo que definas en tu sesión de PowerShell también se definirá en el ámbito global.

Dado que estás en el ámbito global de forma predeterminada, si haces algo que crea otro ámbito, como ejecutar un script o ejecutar una función, se creará un ámbito hijo con el ámbito global como padre. Los ámbitos son como procesos con padres e hijos.

Cualquier cosa que se defina en un ámbito padre, en este caso, el ámbito global, será accesible en el ámbito hijo. Pero estos elementos solo se pueden editar en el ámbito en el que se definieron.

Supongamos que tienes un script llamado Test.ps1. Dentro de este script hay una sola línea como se muestra a continuación.

$a = 'Hello world!'

Cuando ejecutas este script, $a se le asigna un valor en el ámbito local (mientras el script está en ejecución). Cuando se ejecuta Test.ps1, puedes ver a continuación que no puedes hacer referencia a él después de que el script se ejecute. Dado que $a se asignó mientras se encontraba en el ámbito del script, el ámbito global (en la consola interactiva) no puede verlo.

Global scope cannot see the variable

Vamos a llevar este ejemplo un paso más allá y hagamos que el script Test.ps1 se vea así. El script ahora intenta mostrar el valor de $a antes de establecerlo en el mismo ámbito.

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

Para demostrarlo, asigna un valor a $a en la consola interactiva. Esto asigna el valor en el ámbito global. Ahora, cuando se ejecute el script, heredará el ámbito padre (global) y debería poder ver el valor.

Puedes ver a continuación que cuando se ejecuta Test.ps1 (creando un ámbito hijo del ámbito global), puede ver el valor de $a. También puedes ver que el valor de la variable está disponible en el ámbito global, ya que este es donde se estableció. Esto significa que $a está disponible tanto en el ámbito del script (hijo) como en el ámbito del padre (global).

Variable is available in script and global scopes

Recuerda este comportamiento de herencia de ámbito. Hacerlo te ayudará cuando ocurran conflictos de variables, como variables en diferentes ámbitos con el mismo nombre.

Definir y acceder a elementos en ámbitos

Ahora que sabes qué es un ámbito y cómo funcionan, ¿cómo accedes a ellos? Veamos cómo usar PowerShell para establecer un ámbito de variable (y acceder a ellos).

Get/Set-Variable

En PowerShell, hay dos cmdlets que te permiten establecer variables llamadas Get-Variable y Set-Variable. Estos cmdlets te permiten obtener el valor de una variable o definir un valor.

Ambos cmdlets son similares, con un parámetro Name y Scope. Usando estos parámetros, PowerShell te permite establecer y obtener valores de variables en todos los ámbitos.

Ámbitos locales

Para establecer una variable en un ámbito local, usa Set-Variable y proporciona un nombre de variable local y un valor como se muestra a continuación.

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

El ámbito local siempre es el predeterminado, por lo que no usar el parámetro Scope siempre definirá la variable en el ámbito local.

Para obtener el valor de una variable de ámbito local, usa Get-Variable proporcionando el nombre.

PS> Get-Variable -Name a

Name         Value
----         -----
a            foo

Ámbitos privados/Script/Globales

Usarás los mismos parámetros (Name y Value) cuando trabajas con variables privadas, de script y globales también. La única diferencia es que esta vez usarás el parámetro de ámbito para definir explícitamente el ámbito.

Los métodos para establecer una variable de ámbito privado, de script o global son los mismos. Simplemente reemplaza el valor pasado al parámetro Scope como Private, Script o Global.

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

Para recuperar el valor de un script o una variable de ámbito global, usa Get-Variable proporcionando el nombre y el ámbito.

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

Name         Value
----         -----
a            foo

Nota: También puedes hacer referencia a los ámbitos utilizando números de ámbito en lugar de nombres con los cmdlets Get/Set-Variable.

Ámbito “Prefijo”

También puedes recuperar y establecer variables en ámbitos utilizando un atajo. En lugar de utilizar cmdlets de PowerShell, añadirás el prefijo del ámbito a la variable al hacer referencia a ella.

Ámbitos locales

Dado que el ámbito local es siempre el predeterminado, simplemente definir una variable y hacer referencia a ella establecerá y recuperará una variable de ámbito local.

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

foo

Ámbitos privados/Script/Globales

Si deseas definir y hacer referencia a variables de ámbito de script o global, puedes añadir el nombre del ámbito y un punto y coma antes de las variables.

Por ejemplo, para establecer la variable $a en el ámbito global, puedes añadir $global: antes de $a.

$global:a = ‘foo’

Lo mismo se puede hacer para una variable de ámbito de script.

$script:a = ‘foo’

Una vez que las variables se establezcan en el ámbito preferido, las referirás de la misma manera. Además, observa que puedes excluir el prefijo del ámbito si el ámbito definido es el ámbito local.

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

Ámbitos en bloques de script

PowerShell tiene una construcción útil llamada bloques de script. Los bloques de script te permiten mover fragmentos de código y ejecutarlos prácticamente en cualquier lugar.

Al igual que un script PS1, los bloques de script se ejecutan en su propio ámbito de script. Cuando ejecutas un bloque de script, básicamente estás ejecutando un script PS1.

Observe en el ejemplo a continuación donde se define una variable en el ámbito global y luego se intenta sobrescribir en un ámbito de script. Como aprendió anteriormente, esto no funcionará porque un ámbito secundario no puede hacer referencia a un ámbito padre.

A child scope cannot overwrite a parent scope

Este ejemplo muestra que cuando se cambia $a en el bloque de script, la definición de la variable global para $a no se cambia porque el bloque de script es un ámbito secundario de script.

Importación de scripts (Intercambio de ámbitos locales)

PowerShell tiene un concepto llamado importación de scripts. Esto es un método que le permite ejecutar un script PS1 y llevar todo lo que estaría en ámbito de script al ámbito local en su lugar.

Al poner un punto (.) antes de hacer referencia a un script PS1 y ejecutarlo, esto “importa” el contenido del script y lo trae al ámbito local.

Para demostrarlo, tengo un script Test.ps1 nuevamente que define una variable como se muestra a continuación.

$a = 'Hello world!'

En una consola de PowerShell, establezca un valor para una variable $a y luego importe este script como se muestra a continuación. Observe que la variable original fue sobrescrita. PowerShell “fusionó” los dos ámbitos locales juntos.

Original variable overwritten

Uso de la propiedad AllScope (opción)

Ha visto cómo interactuar con elementos en ámbitos específicos, pero hasta ahora cada elemento aún está definido en un solo ámbito. ¿Pero qué pasa si no sabe en qué ámbito se define una variable?

Al definir una variable con el cmdlet Set-Variable, puedes colocar la variable en todos los ámbitos a la vez. Para hacer esto, utiliza el valor AllScope para el parámetro Option.

Para demostrarlo, el script Test.ps1 se ha modificado para establecer una variable en todos los ámbitos. Esa variable se muestra a continuación.

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

A continuación, puedes ver que se está estableciendo un valor para $a en el ámbito global y se ejecuta el script Test.ps1. Sin embargo, en lugar de no tener efecto, el valor de $a ha sido sobrescrito. No solo se ha definido en el ámbito del script (Write-Output $a) sino que también ha sobrescrito el ámbito global.

Variable overwritten in global scope

La opción AllScope es útil pero hay que tener cuidado. Esta opción básicamente elimina el concepto de ámbitos y fusiona todo.

Ámbitos de funciones

Cuando ejecutas una función, todo el código dentro de esa función está en su propio ámbito secundario. Los ámbitos de funciones siguen el mismo comportamiento de hijo/padre que otros ámbitos.

Tener ámbitos separados para cada función es una buena idea. Permite un mejor control sobre los elementos sin tener que preocuparse por conflictos entre ellos. También brinda la ventaja adicional de limpiar automáticamente las variables en una función. Tan pronto como la función se completa, se borran todos los elementos definidos en ella.

Para demostrarlo, copia/pega la siguiente función directamente en la consola de PowerShell.

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

Una vez pegada, ejecuta la función. Observa que no puedes acceder a la variable $var fuera de la función.

PS> Do-Thing
PS> $var

Manteniendo los elementos privados (deshabilitando la herencia)

Típicamente, si una variable está definida en un ámbito superior, esa variable también estará definida en el ámbito inferior. Pero tal vez quieras usar un nombre de variable que ya ha sido definido en uno de los ámbitos a ejecutar en una sesión. En ese caso, puedes elegir un nombre de variable diferente o definir la variable en un ámbito privado para hacerla privada.

A way to use scopes to reduce conflicts is to use the private scope. Using the private scope disables inheritance on that specific variable. When multiple child scopes are created, those child scopes will not see any variables defined in a private scope.

A Test.ps1 script outputs the value of $a as shown below.

Write-Output $a

A continuación, puedes ver cómo defino una variable de ámbito privado en el ámbito global y luego ejecuto el script Test.ps1. Normalmente, cuando defines una variable en un ámbito superior, esa variable estará disponible en el ámbito inferior, pero no es así con una variable de ámbito privado.

En el ejemplo siguiente, puedes ver que el ámbito secundario del script creado al ejecutar el script Test.ps1 no pudo ver la variable $a de ámbito privado definida en el ámbito superior.

Script scope cannot see the private-scoped variable

A diferencia de los ámbitos globales o la opción AllScope en el cmdlet Set-Variable, las variables de ámbito privado son una excelente manera de compartimentar elementos.

Mejores prácticas de ámbito

Es común pensar que definir variables en el ámbito global o usar la opción AllScope es la mejor opción. Después de todo, todas las variables están disponibles en todas partes. No es necesario preocuparse por las complicaciones de los ámbitos. Si bien esto proporciona una mayor libertad para acceder a lo que está definido, puede salirse rápidamente de control y ser difícil de solucionar.

En lugar de tratar de evitar el uso de ámbitos, sigue estos consejos:

  1. En lugar de especificar ámbitos en las funciones, utiliza parámetros para pasar la información requerida a la función.
  2. Mantente dentro del ámbito local tanto como sea posible.
  3. En lugar de definir variables globales desde un script, utiliza el cmdlet Write-Output para mostrar todo y guárdalo en una variable cuando sea necesario desde la consola.

El punto principal aquí es abrazar los ámbitos y aprender a usarlos a tu favor en lugar de tratar de evitarlos.

Lectura adicional

Source:
https://adamtheautomator.com/powershell-scopes/