about_Pipeline_Chain_Operators

Descripción breve

Describe las canalizaciones de encadenamiento con los && operadores y || en PowerShell.

Descripción larga

A partir de PowerShell 7, PowerShell implementa los && operadores y || para encadenar canalizaciones condicionalmente. Estos operadores se conocen en PowerShell como operadores de cadena de canalización y son similares a las listas AND-OR en shells POSIX como bash, zsh y sh, así como símbolos de procesamiento condicional en el Shell de comandos de Windows (cmd.exe).

El operador && ejecuta la canalización derecha, si la canalización izquierda se ha realizado correctamente. Por el contario, el operador || ejecuta la canalización derecha, si la canalización izquierda no se ha realizado correctamente.

Estos operadores usan las variables $? y $LASTEXITCODE para determinar si se produjo un error en una canalización. Esto le permite usarlas con comandos nativos y no solo con cmdlets o funciones. Por ejemplo:

# Create an SSH key pair - if successful copy the public key to clipboard
ssh-keygen -t rsa -b 2048 && Get-Content -Raw ~\.ssh\id_rsa.pub | clip

Ejemplos

Dos comandos correctos

Write-Output 'First' && Write-Output 'Second'
First
Second

Se produce un error en el primer comando, lo que provoca que no se ejecute el segundo

Write-Error 'Bad' && Write-Output 'Second'
Write-Error: Bad

El primer comando se realiza correctamente, por lo que el segundo comando no se ejecuta

Write-Output 'First' || Write-Output 'Second'
First

Se produce un error en el primer comando, por lo que se ejecuta el segundo comando.

Write-Error 'Bad' || Write-Output 'Second'
Write-Error: Bad
Second

El éxito de la canalización se define mediante el valor de la $? variable , que PowerShell establece automáticamente después de ejecutar una canalización en función de su estado de ejecución. Esto significa que los operadores de cadena de canalización tienen la equivalencia siguiente:

Test-Command '1' && Test-Command '2'

funciona igual que

Test-Command '1'; if ($?) { Test-Command '2' }

y

Test-Command '1' || Test-Command '2'

funciona igual que

Test-Command '1'; if (-not $?) { Test-Command '2' }

Asignación desde cadenas de canalización

La asignación de una variable de una cadena de canalización toma la concatenación de todas las canalizaciones de la cadena:

$result = Write-Output '1' && Write-Output '2'
$result
1
2

Si se produce un error de terminación de script durante la asignación desde una cadena de canalización, la asignación no se realiza correctamente:

try
{
    $result = Write-Output 'Value' && $(throw 'Bad')
}
catch
{
    # Do nothing, just squash the error
}

"Result: $result"
Result:

Sintaxis y precedencia del operador

A diferencia de otros operadores, && y || operan en canalizaciones, en lugar de en expresiones como + o -and, por ejemplo.

&&y || tienen una prioridad menor que la canalización (|) o el redireccionamiento (>), pero una prioridad más alta que los operadores de trabajo (), la asignación (&=) o punto y coma (;). Esto significa que las canalizaciones dentro de una cadena de canalización se pueden redirigir individualmente y que todas las cadenas de canalización se pueden en segundo plano, asignarlas a variables o separarlas como instrucciones .

Para usar la sintaxis de prioridad inferior dentro de una cadena de canalización, considere el uso de paréntesis (...). De forma similar, para insertar una instrucción dentro de una cadena de canalización, se puede usar una subexpresión $(...) . Esto puede ser útil para combinar comandos nativos con flujo de control:

foreach ($file in 'file1','file2','file3')
{
    # When find succeeds, the loop breaks
    find $file && Write-Output "Found $file" && $(break)
}
find: file1: No such file or directory
file2
Found file2

A partir de PowerShell 7, se ha cambiado el comportamiento de estas sintaxis para que $? se establezca según lo previsto cuando un comando se realiza correctamente o produce un error entre paréntesis o una subexpresión.

Al igual que la mayoría de los demás operadores de PowerShell, && y || también son asociativos a la izquierda, lo que significa que se agrupan desde la izquierda. Por ejemplo:

Get-ChildItem -Path ./file.txt ||
    Write-Error "file.txt doesn't exist" &&
    Get-Content -Raw ./file.txt

se agrupará como:

(Get-ChildItem -Path ./file.txt || Write-Error "file.txt doesn't exist") &&
    Get-Content -Raw ./file.txt

que es equivalente a:

Get-ChildItem -Path ./file.txt

if (-not $?) { Write-Error "file.txt does not exist" }

if ($?) { Get-Content -Raw ./file.txt }

Interacción de errores

Los operadores de cadena de canalización no absorben errores. Cuando una instrucción de una cadena de canalización produce un error de terminación de script, la cadena de canalización finaliza.

Por ejemplo:

$(throw 'Bad') || Write-Output '2'
Exception: Bad

Incluso cuando se detecta el error, la cadena de canalización sigue finalizando:

try
{
    $(throw 'Bad') || Write-Output '2'
}
catch
{
    Write-Output "Caught: $_"
}
Write-Output 'Done'
Caught: Bad
Done

Si un error no finaliza o solo finaliza una canalización, la cadena de canalización continúa, respetando el valor de $?:

function Test-NonTerminatingError
{
    [CmdletBinding()]
    param()

    $exception = [System.Exception]::new('BAD')
    $errorId = 'BAD'
    $errorCategory = 'NotSpecified'

    $errorRecord = [System.Management.Automation.ErrorRecord]::new(
        $exception, $errorId, $errorCategory, $null
    )

    $PSCmdlet.WriteError($errorRecord)
}

Test-NonTerminatingError || Write-Output 'Second'
Test-NonTerminatingError: BAD
Second

Encadenar canalizaciones en lugar de comandos

Los operadores de cadena de canalización, por su nombre, se pueden usar para encadenar canalizaciones, en lugar de simplemente comandos. Esto coincide con el comportamiento de otros shells, pero puede hacer que el éxito sea más difícil de determinar:

function Test-NotTwo
{
    [CmdletBinding()]
    param(
      [Parameter(ValueFromPipeline)]
      $Input
    )

    process
    {
        if ($Input -ne 2)
        {
            return $Input
        }

        $exception = [System.Exception]::new('Input is 2')
        $errorId = 'InputTwo'
        $errorCategory = 'InvalidData'

        $errorRecord = [System.Management.Automation.ErrorRecord]::new(
            $exception, $errorId, $errorCategory, $null
        )

        $PSCmdlet.WriteError($errorRecord)
    }
}

1,2,3 | Test-NotTwo && Write-Output 'All done!'
1
Test-NotTwo : Input is 2
3

Tenga en cuenta que no se ejecuta, ya Test-NotTwo que Write-Output 'All done!' se considera que se ha producido un error después de generar el error de no terminación.

Consulte también