Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
A menudo, PowerShell $null
parece ser sencillo, pero tiene muchos matices. Analicemos detenidamente $null
para que sepas qué ocurre cuando encuentras inesperadamente un valor de $null
.
Nota:
La versión original de este artículo apareció en el blog escrito por @KevinMarquette. El equipo de PowerShell agradece a Kevin por compartir este contenido con nosotros. Consulte su blog en PowerShellExplained.com.
¿Qué es NULL?
Puede considerar NULL como un valor desconocido o vacío. Una variable es NULL hasta que se le asigna un valor o un objeto. Esto puede ser importante porque hay algunos comandos que requieren un valor y generan errores si el valor es NULL.
PowerShell $null
$null
es una variable automática en PowerShell que se usa para representar NULL. Puede asignarla a variables, usarla en comparaciones y usarla como marcador de posición para NULL en una colección.
PowerShell trata $null
como un objeto con un valor NULL. Esto es diferente de lo que puede esperar si proviene de otro idioma.
Ejemplos de $null
Cada vez que intente usar una variable que no se haya inicializado, el valor es $null
. Esta es una de las formas más comunes en que los $null
valores entran en el código.
PS> $null -eq $undefinedVariable
True
Si se produce un error en el tipo de un nombre de variable, PowerShell lo ve como una variable diferente y el valor es $null
.
La otra forma de encontrar $null
valores es cuando proceden de otros comandos que no proporcionan ningún resultado.
PS> function Get-Nothing {}
PS> $value = Get-Nothing
PS> $null -eq $value
True
Impacto de $null
$null
los valores afectan al código de forma diferente en función de dónde se muestren.
En cadenas
Si usa $null
en una cadena, se trata de un valor en blanco (o una cadena vacía).
PS> $value = $null
PS> Write-Output "'The value is $value'"
'The value is '
Esta es una de las razones por las que me gusta colocar corchetes en torno a las variables al usarlos en los mensajes de registro. Es aún más importante identificar los bordes de los valores de las variables cuando el valor está al final de la cadena.
PS> $value = $null
PS> Write-Output "The value is [$value]"
The value is []
Esto hace que las cadenas vacías y los valores $null
vacíos se detecten fácilmente.
En ecuación numérica
Cuando se usa un $null
valor en una ecuación numérica, los resultados no son válidos si no dan un error. A veces, $null
se evalúa como 0
y otras veces hace que $null
sea todo el resultado.
Este es un ejemplo con multiplicación que proporciona 0 o $null
según el orden de los valores.
PS> $null * 5
PS> $null -eq ( $null * 5 )
True
PS> 5 * $null
0
PS> $null -eq ( 5 * $null )
False
En el lugar de una recopilación
Una colección permite usar un índice para acceder a los valores. Si intenta indexar en una colección que es realmente null
, obtendrá este error: Cannot index into a null array
.
PS> $value = $null
PS> $value[10]
Cannot index into a null array.
At line:1 char:1
+ $value[10]
+ ~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
Si tiene una colección pero intenta acceder a un elemento que no está en la colección, obtendrá un $null
resultado.
$array = @( 'one','two','three' )
$null -eq $array[100]
True
En lugar de un objeto
Si intenta tener acceso a una propiedad o una subpropiedad de un objeto que no tiene la propiedad especificada, obtendrá un $null
valor como lo haría con una variable indefinida. No importa si la variable es $null
o un objeto real en este caso.
PS> $null -eq $undefined.Some.Fake.Property
True
PS> $date = Get-Date
PS> $null -eq $date.Some.Fake.Property
True
Método en una expresión con valores NULL
La llamada a un método en un objeto $null
produce RuntimeException
.
PS> $value = $null
PS> $value.ToString()
You cannot call a method on a null-valued expression.
At line:1 char:1
+ $value.ToString()
+ ~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Cada vez que veo la frase You cannot call a method on a null-valued expression
, lo primero que buscaré son lugares donde estoy llamando a un método en una variable sin comprobarlo primero para $null
.
Búsqueda de $null
Es posible que haya observado que siempre coloco $null
a la izquierda al buscar $null
en mis ejemplos. Esto es intencionado y aceptado como procedimiento recomendado de PowerShell. Hay algunos escenarios en los que colocarlo a la derecha no le da el resultado esperado.
Examine este ejemplo siguiente e intente predecir los resultados:
if ( $value -eq $null )
{
'The array is $null'
}
if ( $value -ne $null )
{
'The array is not $null'
}
Si no defina $value
, el primero se evalúa como $true
y nuestro mensaje es The array is $null
. La trampa aquí es que es posible crear un $value
que permita que ambos sean $false
$value = @( $null )
En este caso, $value
es una matriz que contiene un $null
.
-eq
Comprueba todos los valores de la matriz y devuelve el $null
que coincide. Esto da como resultado $false
.
-ne
devuelve todo lo que no coincide $null
y, en este caso, no hay resultados (esto también se evalúa como $false
). Ninguno de ellos es $true
, aunque parezca que uno de ellos debe serlo.
No solo podemos crear un valor que haga que ambos se evalúen como $false
, es posible crear un valor donde ambos se evalúan como $true
. Mathias Jessen (@IISResetMe) tiene una buena publicación que profundiza en ese escenario.
PSScriptAnalyzer y VS Code
El módulo PSScriptAnalyzer tiene una regla que comprueba si hay este problema denominado PSPossibleIncorrectComparisonWithNull
.
PS> Invoke-ScriptAnalyzer ./myscript.ps1
RuleName Message
-------- -------
PSPossibleIncorrectComparisonWithNull $null should be on the left side of equality comparisons.
Dado que VS Code también usa las reglas de PSScriptAnalyser, también resalta o lo identifica como un problema en el script.
Comprobación if simple
Una forma común de comprobar si hay un valor que no es $null es usar una instrucción simple if()
sin la comparación.
if ( $value )
{
Do-Something
}
Si el valor es $null
, se evalúa como $false
. Resulta fácil de leer, pero fíjese en que esté buscando exactamente lo que espera que busque. Leí esa línea de código como:
Si
$value
tiene un valor.
Pero eso no es toda la historia. De hecho, esa línea dice:
Si
$value
no es$null
,0
,$false
, una cadena vacía o una matriz vacía.
Este es un ejemplo más completo de esa instrucción.
if ( $null -ne $value -and
$value -ne 0 -and
$value -ne '' -and
($value -isnot [array] -or $value.Length -ne 0) -and
$value -ne $false )
{
Do-Something
}
Es perfectamente correcto usar una comprobación básica if
siempre y cuando recuerde que esos otros valores cuentan como $false
y no solo que una variable tiene un valor.
He tenido este problema al refactorizar código hace unos días. Tenía una comprobación de propiedad básica como esta.
if ( $object.Property )
{
$object.Property = $value
}
Quería asignar un valor a la propiedad del objeto solo si existía. En la mayoría de los casos, el objeto original tenía un valor que se evaluaría como $true
en la sentencia if
. Pero me encontré con un problema en el que el valor ocasionalmente no se establecía. Depuré el código y vi que el objeto tenía la propiedad, pero era un valor de cadena en blanco. Esto impedía que se actualizara con la lógica anterior. Así que añadí una comprobación adecuada $null
y todo funcionó.
if ( $null -ne $object.Property )
{
$object.Property = $value
}
Son pequeños errores como estos que son difíciles de detectar y me llevan a comprobar con ahínco los valores de $null
.
$null. Contar
Si intenta acceder a una propiedad de un valor $null
, esa propiedad también es $null
. La Count
propiedad es la excepción a esta regla.
PS> $value = $null
PS> $value.Count
0
Cuando tienes un valor $null
, entonces el Count
es 0
. PowerShell agrega esta propiedad especial.
[PSCustomObject] Contar
Casi todos los objetos de PowerShell tienen esa Count
propiedad. Una excepción importante es la [pscustomobject]
en Windows PowerShell 5.1 (esto se corrigió en PowerShell 6.0). Como no tiene una Count
propiedad, obtendrá un $null
valor si intenta usarlo. Menciono esto aquí para que no intentes usar Count
en lugar de una $null
verificación.
Al ejecutar este ejemplo en Windows PowerShell 5.1 y PowerShell 6.0 se proporcionan resultados diferentes.
$value = [pscustomobject]@{Name='MyObject'}
if ( $value.Count -eq 1 )
{
"We have a value"
}
Valor null enumerable
Hay un tipo especial de $null
que actúa de forma diferente a la de los demás. Voy a llamarlo null enumerable, pero es realmente un System.Management.Automation.Internal.AutomationNull.
Este valor NULL enumerable es el que obtiene como resultado de una función o bloque de script que no devuelve nada (un resultado nulo).
PS> function Get-Nothing {}
PS> $nothing = Get-Nothing
PS> $null -eq $nothing
True
Si lo compara con $null
, obtendrá un $null
valor. Cuando se usa en una evaluación donde un valor es requerido, el valor siempre es $null
. Pero si lo coloca dentro de una matriz, se trata igual que una matriz vacía.
PS> $containEmpty = @( @() )
PS> $containNothing = @($nothing)
PS> $containNull = @($null)
PS> $containEmpty.Count
0
PS> $containNothing.Count
0
PS> $containNull.Count
1
Puede tener una matriz que contenga un $null
valor y su Count
es 1
. Pero si coloca una matriz vacía dentro de una matriz, no se cuenta como un elemento. El recuento es 0
.
Si trata el valor NULL enumerable como una colección, está vacío.
Si pasa un valor null enumerable a un parámetro de función que no está fuertemente tipado, PowerShell convierte el valor null enumerable en un valor $null
de manera predeterminada. Esto significa que dentro de la función, el valor se trata como $null
en lugar del tipo System.Management.Automation.Internal.AutomationNull .
Tubería
El lugar principal que ve la diferencia es cuando se usa la canalización. Puede canalizar un $null
valor pero no un valor NULL enumerable.
PS> $null | ForEach-Object{ Write-Output 'NULL Value' }
'NULL Value'
PS> $nothing | ForEach-Object{ Write-Output 'No Value' }
En función del código, debe tener en cuenta $null
en la lógica.
Compruebe primero $null
.
- Filtre null en la canalización (
... | where {$null -ne $_} | ...
). - Adminístrelo en la función de canalización.
foreach
Una de mis características favoritas de foreach
es que no realiza enumeración sobre una colección de $null
.
foreach ( $node in $null )
{
#skipped
}
Esto me ahorra tener que $null
comprobar la colección antes de enumerarla. Si tiene una colección de $null
valores, $node
todavía puede ser $null
.
foreach
comenzó a funcionar de esta manera con PowerShell 3.0. Si tiene una versión anterior, entonces este no es el caso. Se trata de uno de los cambios importantes que se deben tener en cuenta al volver a portar el código para la compatibilidad con la versión 2.0.
Tipos de valor
Técnicamente, solo los tipos de referencia pueden ser $null
. Pero PowerShell es muy generoso y permite que las variables sean de cualquier tipo. Si decide establecer un tipo de valor de forma inflexible, no puede ser $null
.
PowerShell convierte $null
en un valor predeterminado para muchos tipos.
PS> [int]$number = $null
PS> $number
0
PS> [bool]$boolean = $null
PS> $boolean
False
PS> [string]$string = $null
PS> $string -eq ''
True
Hay algunos tipos que no tienen una conversión válida de $null
. Estos tipos generan un Cannot convert null to type
error.
PS> [datetime]$date = $null
Cannot convert null to type "System.DateTime".
At line:1 char:1
+ [datetime]$date = $null
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : MetadataError: (:) [], ArgumentTransformationMetadataException
+ FullyQualifiedErrorId : RuntimeException
Parámetros de función
El uso de valores con establecimiento inflexible de tipos en los parámetros de función es muy común. Por lo general, aprendemos a definir los tipos de nuestros parámetros incluso si no tendemos a definir los tipos de otras variables en nuestros scripts. Es posible que ya tenga algunas variables con establecimiento inflexible de tipos en las funciones y que no se haya dado cuenta.
function Do-Something
{
param(
[string] $Value
)
}
Tan pronto como establezca el tipo del parámetro como string
, el valor nunca puede ser $null
. Es habitual comprobar si un valor es $null
ver si el usuario proporcionó un valor o no.
if ( $null -ne $Value ){...}
$Value
es una cadena ''
vacía cuando no se proporciona ningún valor. Use la variable $PSBoundParameters.Value
automática en su lugar.
if ( $null -ne $PSBoundParameters.Value ){...}
$PSBoundParameters
solo contiene los parámetros especificados cuando se llamó a la función.
También puede utilizar el método ContainsKey
para comprobar la propiedad.
if ( $PSBoundParameters.ContainsKey('Value') ){...}
No es nulo ni está vacío
Si el valor es una cadena, puede usar una función de cadena estática para comprobar si el valor es $null
o una cadena vacía al mismo tiempo.
if ( -not [string]::IsNullOrEmpty( $value ) ){...}
Me encuentro usando esto a menudo cuando sé que el tipo de valor debe ser una cadena.
Cuando compruebo $null
Soy un scripter defensivo. Cada vez que llame a una función y asígnela a una variable, la comprué para $null
.
$userList = Get-ADUser kevmar
if ($null -ne $userList){...}
Prefiero mucho usar if
o foreach
sobre usar try/catch
. No me equivoques, todavía uso try/catch
mucho. Pero si puedo probar una condición de error o un conjunto vacío de resultados, puedo permitir que mi control de excepciones sea para excepciones verdaderas.
También tiendo a buscar $null
antes de indexar en un valor o llamar a métodos en un objeto. Estas dos acciones no se pueden realizar para un $null
objeto, por lo que creo que es importante validarlas primero. Ya he tratado esos escenarios anteriormente en esta publicación.
Sin escenario de resultados
Es importante saber que las diferentes funciones y comandos controlan el escenario sin resultados de forma diferente. Muchos comandos de PowerShell devuelven el valor NULL enumerable y un error en la secuencia de errores. Pero otros inician excepciones o proporcionan un objeto de estado. Todavía depende de ti saber cómo los comandos que utilizas gestionan las situaciones de errores y de no resultados.
Inicialización en $null
Un hábito que he recogido es inicializar todas mis variables antes de usarlas. Debe hacerlo en otros idiomas. En la parte superior de mi función o al entrar en un foreach
bucle, defino todos los valores que estoy usando.
Este es un escenario en el que quiero que eche un vistazo. Es un ejemplo de un error que tuve que perseguir antes.
function Do-Something
{
foreach ( $node in 1..6 )
{
try
{
$result = Get-Something -Id $node
}
catch
{
Write-Verbose "[$result] not valid"
}
if ( $null -ne $result )
{
Update-Something $result
}
}
}
La expectativa aquí es que Get-Something
devuelva un resultado o un valor NULL enumerable. Si se produce un error, lo registramos. A continuación, comprobamos para asegurarnos de que tenemos un resultado válido antes de procesarlo.
El error presente en este código ocurre cuando Get-Something
lanza una excepción y no asigna un valor a $result
. Se produce un error antes de la asignación, por lo que ni siquiera se asigna $null
a la $result
variable .
$result
todavía contiene el valor válido $result
anterior de otras iteraciones.
Update-Something
para ejecutar varias veces en el mismo objeto de este ejemplo.
He establecido $result
en $null
justo dentro del foreach
bucle antes de usarlo para mitigar este problema.
foreach ( $node in 1..6 )
{
$result = $null
try
{
...
Problemas de ámbito
Esto también ayuda a mitigar los problemas de ámbito. En ese ejemplo, asignamos valores a $result
una y otra vez en un bucle. Sin embargo, dado que PowerShell permite que los valores de variable de fuera de la función interfieran en el ámbito de la función actual, su inicialización dentro de la función mitiga los errores que se pueden producir de este modo.
Una variable no inicializada en la función no es $null
si está establecida en un valor en un ámbito primario.
El ámbito primario podría ser otra función que llama a la función y usa los mismos nombres de variable.
Si tomo ese mismo Do-something
ejemplo y quite el bucle, terminaría con algo similar a este ejemplo:
function Invoke-Something
{
$result = 'ParentScope'
Do-Something
}
function Do-Something
{
try
{
$result = Get-Something -Id $node
}
catch
{
Write-Verbose "[$result] not valid"
}
if ( $null -ne $result )
{
Update-Something $result
}
}
Si la llamada a Get-Something
produce una excepción, la comprobación de $null
encuentra $result
en Invoke-Something
. Al inicializar el valor dentro de la función, se mitiga este problema.
La nomenclatura de variables es difícil y es habitual que un autor use los mismos nombres de variable en varias funciones. Sé que uso $node
,$result
$data
todo el tiempo. Por lo tanto, sería muy fácil que los valores de diferentes ámbitos se muestren en lugares donde no deberían ser.
Redirección de la salida a $null
He estado hablando acerca de los valores $null
durante todo este artículo, pero el tema queda incompleto si no se menciona la redirección de la salida a $null
. Hay ocasiones en las que tiene comandos que generan información o objetos que desea suprimir. Redirigir la salida a $null
lo hace.
Out-Null
El comando Out-Null es la forma integrada de redirigir los datos de canalización a $null
.
New-Item -Type Directory -Path $path | Out-Null
Asignación a $null
Puede asignar los resultados de un comando a $null
para el mismo efecto que mediante Out-Null
.
$null = New-Item -Type Directory -Path $path
Dado que $null
es un valor constante, nunca se puede sobrescribir. No me gusta la forma en que se ve en mi código, pero a menudo funciona más rápido que Out-Null
.
Redireccionamiento a $null
También puede usar el operador de redireccionamiento para enviar la salida a $null
.
New-Item -Type Directory -Path $path > $null
Si está tratando con ejecutables de línea de comandos que se generan en los diferentes flujos. Puede redirigir todas las secuencias de salida a $null
como esta:
git status *> $null
Resumen
He tratado mucho sobre este tema y sé que este artículo está más fragmentado que la mayoría de mis inmersiones profundas. Esto se debe a que los valores de $null
pueden aparecer en muchos lugares diferentes de PowerShell y todos los matices son específicos del lugar donde los encuentre. Espero que después de haberlo leído entienda mejor $null
y conozca los escenarios más complejos con los que puede encontrarse.