¿Por qué los operadores de comparación de PowerShell no enumeran colecciones de tamaño 1?

Resuelto Jeremy Fortune asked hace 5 años • 2 respuestas

Al verificar la nulidad de variables y colecciones de variables, los operadores de comparación parecen enumerar colecciones de tamaño 2 o más:

> if ( @( $null, $null ) -eq $null ) { $True } else { $False }
True

Pero no lo hacen para colecciones de tamaño 1:

> if ( @( $null ) -eq $null ) { $True } else { $False }
False

Soy consciente de que es una buena práctica realizar una comparación nula utilizando el lado izquierdo ( $null -eq @( $null )), pero ¿alguien puede explicar qué está sucediendo aquí? Sospecho que está sucediendo algo más sutil que afecta el resto del código que escribo.

¿Por qué estos dos resultados son diferentes?

Jeremy Fortune avatar Nov 02 '18 02:11 Jeremy Fortune
Aceptado

tl; dr

[bool]En contextos y conversiones booleanos condicionales/implícitos de PowerShell :

  • Las matrices de un solo elemento se tratan como escalares : es decir, su único elemento se interpreta como booleano. [1]

  • Las matrices de más de 2 elementos siempre son $true, independientemente de su contenido.


Con una matriz como LHS , los operadores que reconocen la matriz, como -eqinvariablemente, también generan una matriz .

Dado que los elementos de su matriz son todos $nully se compara con $null, su comparación es una operación no operativa efectiva (por ejemplo, @( $null ) -eq $nullda como resultado @( $null )) y sus condicionales son equivalentes a:

[bool] @( $null, $null ) # -> $true - array with 2+ elements is always $True
[bool] @( $null )        # -> $false(!) - treated like: [bool] $null

Quizás sea sorprendente que la lógica booleana implícita aplique la lógica de canalización a una matriz :

Es decir, una matriz de un solo elemento se desenvuelve (conceptualmente) y su elemento se interpreta como booleano.

Por lo tanto, [bool] @( $null )se trata igual que [bool] $null, que es $false.

Generalmente, @( <one-and-only-element> )(o , <one-and-only-element>) se trata igual que <one-and-only-element>en un contexto booleano.

Por el contrario, si una matriz tiene 2 o más elementos , siempre está $trueen un contexto booleano , incluso si se consideraran todos sus elementos individualmente $false.


Solución alternativa para probar si una matriz arbitraria está vacía :

Base su condicional en la .Countpropiedad:

if ( (<array>).Count ) { $true } else { $false }

Podrías agregar -gt 0, pero eso no es estrictamente necesario, porque cualquier valor distinto de cero es implícitamente $true.

Aplicado a su ejemplo:

PS> if ( ( @($null) -eq $null ).Count ) { $true } else { $false }
True

Probando que un valor arbitrario sea un (escalar) $null:

if ($null -eq <value>) { $true } else { $false }

Tenga en cuenta que $nulldebe usarse como LHS para evitar que la lógica de filtrado de matriz tenga efecto; debe <value>ser una matriz.

Esa es también la razón por la cual Visual Studio Code con la extensión PowerShell recomienda "$null debe estar en el lado izquierdo de las comparaciones" si escribe algo como $var -eq $null.


[1] Resumen de conversión a booleana :

  • Entre escalares (no colecciones) :

    • Lo siguiente está implícito$false :

      • ''/ ""(cuerda vacía)

      • 0(de cualquier tipo numérico).

      • $null, incluido el valor "nulo enumerable" que indica la ausencia de salida de un comando (consulte esta respuesta para obtener información general); por ejemplo, ambos
        [bool] $nully [bool] (Get-ChildItem NoSuchFiles*)rendimiento $false.

        • Error : comparar $nullel "nulo enumerable" con un booleano explícitamente es-eq siempre ,$false incluso con $nullel RHS (a pesar de que el RHS normalmente se ve obligado al tipo de LHS):

          # !! -> $false
          # By contrast, `$false -eq [bool] $null` is $true
          $false -eq $null
          
          # Ditto for the "enumerable null"
          # !! -> $false
          $false -eq (Get-ChildItem NoSuchFiles*)
          
    • Error : cualquier cadena que no esté vacía es siempre$true :

      • por ejemplo, [bool] 'False'es$true

      • Tenga en cuenta que esto difiere del análisis explícito de cadenas : [bool]::Parse('false')devuelve $false(y $truepara 'true', pero no reconoce nada más).

    • Las instancias de cualquier otro tipo que no sea de colección son siempre$true implícitamente , incluidas las de tipo [pscustomobject]y [hashtable](que PowerShell trata como un objeto único, no como una colección de entradas).

      • Desafortunadamente, esto incluye tipos que definen [bool]operadores de conversión .NET explícitos, lo que significa que estos operadores, en su mayoría, no se respetan; ver esta respuesta .
  • Entre colecciones como matrices (más exactamente, tipos similares a colecciones que implementan la IListinterfaz; consulte el código fuente ):
    • Las colecciones vacías siempre$false son.

    • Error : las colecciones de un solo elemento se evalúan como ese elemento :

      • Si el único elemento es un escalar (no colección): su valor booleano.

        • Por ejemplo, [bool] @(0)es $false, mientras que [bool] @(42)es$true
      • Si ese elemento es en sí mismo una colección : $truesi tiene al menos 1 elemento, independientemente de cuál sea ese elemento .

        • Por ejemplo, [bool] (,, $false)es $true(una matriz irregular: una matriz de un solo elemento cuyo único elemento es en sí mismo una matriz (de un solo elemento))
    • Las colecciones de más de 2 elementos siempre $true son , independientemente de los valores de sus elementos .

      • Por ejemplo, [bool] @($null, $false)es $true.
mklement0 avatar Nov 01 '2018 19:11 mklement0

Los siguientes elementos se evalúan como $false:

@()
0
$null
$false
''

En su primer ejemplo:

@($null, $null) -eq $null

Esto evalúa $null, $nullcuál es una colección distinta de cero, por lo que lo es $true. Puedes observar esto con lo siguiente:

[bool]($null, $null)

En su segundo ejemplo, lo que está observando es el filtrado de una matriz como en el primer caso, pero devolviendo un escalar (en lugar de una matriz) ya que solo un elemento de la matriz coincidió con el filtro:

@($null) -eq $null

Esto se evalúa como, @($null)pero PowerShell lo evalúa como un escalar en un contexto booleano, por lo que devuelve $false, observado por:

[bool]@($null)

Nota al pie: en powershell v2 , hubo un error con el filtrado que generó la comparación $nullde la izquierda . $nullEste error provocó que if/elselos bloques se omitieran por completo.

Maximilian Burszley avatar Nov 01 '2018 19:11 Maximilian Burszley