about_Pipelines

Descripción breve

Combinación de comandos en canalizaciones en PowerShell

Descripción larga

Una canalización es una serie de comandos conectados por operadores de canalización () (|ASCII 124). Cada operador de canalización envía los resultados del comando anterior al siguiente comando.

La salida del primer comando se puede enviar para su procesamiento como entrada al segundo comando. Y esa salida se puede enviar a otro comando. El resultado es una cadena de comandos compleja o una canalización compuesta por una serie de comandos simples.

Por ejemplo,

Command-1 | Command-2 | Command-3

En este ejemplo, los objetos que Command-1 emite se envían a Command-2. Command-2 procesa los objetos y los envía a Command-3. Command-3 procesa los objetos y los envía a la canalización. Dado que no hay más comandos en la canalización, los resultados se muestran en la consola.

En una canalización, los comandos se procesan en orden de izquierda a derecha. El procesamiento se controla como una sola operación y la salida se muestra a medida que se genera.

A continuación se muestra un ejemplo sencillo. El comando siguiente obtiene el proceso de Bloc de notas y, a continuación, lo detiene.

Por ejemplo,

Get-Process notepad | Stop-Process

El primer comando usa el Get-Process cmdlet para obtener un objeto que representa el proceso de Bloc de notas. Usa un operador de canalización (|) para enviar el objeto de proceso al Stop-Process cmdlet , que detiene el proceso de Bloc de notas. Observe que el Stop-Process comando no tiene un parámetro Name o ID para especificar el proceso, ya que el proceso especificado se envía a través de la canalización.

Este ejemplo de canalización obtiene los archivos de texto del directorio actual, selecciona solo los archivos que tienen más de 10 000 bytes de longitud, los ordena por longitud y muestra el nombre y la longitud de cada archivo de una tabla.

Get-ChildItem -Path *.txt |
  Where-Object {$_.length -gt 10000} |
    Sort-Object -Property length |
      Format-Table -Property name, length

Esta canalización consta de cuatro comandos en el orden especificado. En la ilustración siguiente se muestra la salida de cada comando a medida que se pasa al siguiente comando de la canalización.

Get-ChildItem -Path *.txt
| (FileInfo objects for *.txt)
V
Where-Object {$_.length -gt 10000}
| (FileInfo objects for *.txt)
| (      Length > 10000      )
V
Sort-Object -Property Length
| (FileInfo objects for *.txt)
| (      Length > 10000      )
| (     Sorted by length     )
V
Format-Table -Property name, length
| (FileInfo objects for *.txt)
| (      Length > 10000      )
| (     Sorted by length     )
| (   Formatted in a table   )
V

Name                       Length
----                       ------
tmp1.txt                    82920
tmp2.txt                   114000
tmp3.txt                   114000

Uso de canalizaciones

La mayoría de los cmdlets de PowerShell están diseñados para admitir canalizaciones. En la mayoría de los casos, puede canalizar los resultados de un cmdlet Get a otro cmdlet del mismo nombre. Por ejemplo, puede canalizar la salida del Get-Service cmdlet a los Start-Service cmdlets o Stop-Service .

Esta canalización de ejemplo inicia el servicio WMI en el equipo:

Get-Service wmi | Start-Service

Por otro ejemplo, puede canalizar la salida de o Get-ChildItem dentro del proveedor del Registro de Get-Item PowerShell al New-ItemProperty cmdlet . En este ejemplo se agrega una nueva entrada del Registro, NoOfEmployees, con un valor de 8124, a la clave del Registro MyCompany .

Get-Item -Path HKLM:\Software\MyCompany |
  New-ItemProperty -Name NoOfEmployees -Value 8124

Muchos de los cmdlets de utilidad, como Get-Member, Where-Object, Sort-Object, Group-Objecty Measure-Object se usan casi exclusivamente en canalizaciones. Puede canalizar cualquier tipo de objeto a estos cmdlets. En este ejemplo se muestra cómo ordenar todos los procesos del equipo por el número de identificadores abiertos en cada proceso.

Get-Process | Sort-Object -Property handles

Puede canalizar objetos a los cmdlets de formato, exportación y salida, como Format-List, Format-Table, Export-Clixml, Export-CSVy Out-File.

En este ejemplo se muestra cómo usar el Format-List cmdlet para mostrar una lista de propiedades de un objeto de proceso.

Get-Process winlogon | Format-List -Property *

También puede canalizar la salida de los comandos nativos a los cmdlets de PowerShell. Por ejemplo:

PS> ipconfig.exe | Select-String -Pattern 'IPv4'

   IPv4 Address. . . . . . . . . . . : 172.24.80.1
   IPv4 Address. . . . . . . . . . . : 192.168.1.45
   IPv4 Address. . . . . . . . . . . : 100.64.108.37

Importante

Los flujos success y Error son similares a los flujos stdin y stderr de otros shells. Sin embargo, stdin no está conectado a la canalización de PowerShell para la entrada. Para obtener más información, consulte about_Redirection.

Con un poco de práctica, encontrará que combinar comandos simples en canalizaciones ahorra tiempo y escritura, y hace que el scripting sea más eficaz.

Funcionamiento de las canalizaciones

En esta sección se explica cómo se enlazan los objetos de entrada a parámetros de cmdlet y se procesan durante la ejecución de la canalización.

Acepta la entrada de canalización.

Para admitir la canalización, el cmdlet receptor debe tener un parámetro que acepte la entrada de canalización. Use el Get-Help comando con las opciones Full o Parameter para determinar qué parámetros de un cmdlet aceptan la entrada de canalización.

Por ejemplo, para determinar cuál de los parámetros del cmdlet acepta la entrada de canalización Start-Service , escriba:

Get-Help Start-Service -Full

o

Get-Help Start-Service -Parameter *

La ayuda del Start-Service cmdlet muestra que solo los parámetros InputObject y Name aceptan entradas de canalización.

-InputObject <ServiceController[]>
Specifies ServiceController objects representing the services to be started.
Enter a variable that contains the objects, or type a command or expression
that gets the objects.

Required?                    true
Position?                    0
Default value                None
Accept pipeline input?       True (ByValue)
Accept wildcard characters?  false

-Name <String[]>
Specifies the service names for the service to be started.

The parameter name is optional. You can use Name or its alias, ServiceName,
or you can omit the parameter name.

Required?                    true
Position?                    0
Default value                None
Accept pipeline input?       True (ByPropertyName, ByValue)
Accept wildcard characters?  false

Al enviar objetos a través de la canalización a Start-Service, PowerShell intenta asociar los objetos a los parámetros InputObject y Name .

Métodos de aceptación de entrada de canalización

Los parámetros de los cmdlets pueden aceptar la entrada de canalización de una de las dos maneras diferentes:

  • ByValue: el parámetro acepta valores que coinciden con el tipo de .NET esperado o que se puede convertir a ese tipo.

    Por ejemplo, el parámetro Name de acepta la entrada de Start-Service canalización por valor. Puede aceptar objetos de cadena u objetos que se pueden convertir en cadenas.

  • ByPropertyName: el parámetro acepta solo la entrada cuando el objeto de entrada tiene una propiedad del mismo nombre que el parámetro .

    Por ejemplo, el parámetro Name de Start-Service puede aceptar objetos que tienen una propiedad Name . Para enumerar las propiedades de un objeto, canalice a Get-Member.

Algunos parámetros pueden aceptar objetos por valor o nombre de propiedad, lo que facilita la entrada de la canalización.

Enlace de parámetros

Al canalizar objetos de un comando a otro, PowerShell intenta asociar los objetos canalados con un parámetro del cmdlet receptor.

El componente de enlace de parámetros de PowerShell asocia los objetos de entrada con parámetros de cmdlet según los criterios siguientes:

  • El parámetro debe aceptar la entrada de una canalización.
  • El parámetro debe aceptar el tipo de objeto que se envía o un tipo que se puede convertir al tipo esperado.
  • El parámetro no se usó en el comando .

Por ejemplo, el Start-Service cmdlet tiene muchos parámetros, pero solo dos de ellos, Name y InputObject aceptan entradas de canalización. El parámetro Name toma cadenas y el parámetro InputObject toma objetos de servicio. Por lo tanto, puede canalizar cadenas, objetos de servicio y objetos con propiedades que se pueden convertir en objetos de cadena o servicio.

PowerShell administra el enlace de parámetros de la forma más eficaz posible. No puede sugerir ni forzar que PowerShell se enlace a un parámetro específico. Se produce un error en el comando si PowerShell no puede enlazar los objetos canalados.

Para obtener más información sobre cómo solucionar errores de enlace, consulte Investigación de errores de canalización más adelante en este artículo.

Procesamiento de uno a uno en tiempo

La canalización de objetos a un comando es muy similar al uso de un parámetro del comando para enviar los objetos. Echemos un vistazo a un ejemplo de canalización. En este ejemplo, se usa una canalización para mostrar una tabla de objetos de servicio.

Get-Service | Format-Table -Property Name, DependentServices

Funcionalmente, esto es como usar el parámetro InputObject de Format-Table para enviar la colección de objetos.

Por ejemplo, podemos guardar la colección de servicios en una variable que se pasa mediante el parámetro InputObject .

$services = Get-Service
Format-Table -InputObject $services -Property Name, DependentServices

O bien, podemos insertar el comando en el parámetro InputObject .

Format-Table -InputObject (Get-Service) -Property Name, DependentServices

Sin embargo, hay una diferencia importante. Al canalizar varios objetos a un comando, PowerShell envía los objetos al comando uno a uno. Cuando se usa un parámetro de comando, los objetos se envían como un único objeto de matriz. Esta diferencia menor tiene consecuencias significativas.

Al ejecutar una canalización, PowerShell enumera automáticamente cualquier tipo que implemente la IEnumerable interfaz o su homólogo genérico. Los elementos enumerados se envían a través de la canalización de uno en uno. PowerShell también enumera los tipos System.Data.DataTable a través de la Rows propiedad .

Hay algunas excepciones a la enumeración automática.

  • Debe llamar al GetEnumerator() método para las tablas hash, los tipos que implementan la IDictionary interfaz o su homólogo genérico y los tipos System.Xml.XmlNode .
  • La clase System.String implementa IEnumerable, pero PowerShell no enumera los objetos de cadena.

En los ejemplos siguientes, una matriz y una tabla hash se canalizan al Measure-Object cmdlet para contar el número de objetos recibidos de la canalización. La matriz tiene varios miembros y la tabla hash tiene varios pares clave-valor. Solo se enumera la matriz una a la vez.

@(1,2,3) | Measure-Object
Count    : 3
Average  :
Sum      :
Maximum  :
Minimum  :
Property :
@{"One"=1;"Two"=2} | Measure-Object
Count    : 1
Average  :
Sum      :
Maximum  :
Minimum  :
Property :

Del mismo modo, si canaliza varios objetos de proceso del Get-Process cmdlet al Get-Member cmdlet , PowerShell envía cada objeto de proceso, de uno en uno, a Get-Member. Get-Member muestra la clase .NET (tipo) de los objetos de proceso y sus propiedades y métodos.

Get-Process | Get-Member
TypeName: System.Diagnostics.Process

Name      MemberType     Definition
----      ----------     ----------
Handles   AliasProperty  Handles = Handlecount
Name      AliasProperty  Name = ProcessName
NPM       AliasProperty  NPM = NonpagedSystemMemorySize
...

Nota:

Get-Member elimina duplicados, por lo que si los objetos son todos del mismo tipo, solo muestra un tipo de objeto.

Sin embargo, si usa el parámetro InputObject de Get-Member, Get-Member recibe una matriz de objetos System.Diagnostics.Process como una sola unidad. Muestra las propiedades de una matriz de objetos. (Anote el símbolo de matriz ([]) después del nombre del tipo System.Object ).

Por ejemplo,

Get-Member -InputObject (Get-Process)
TypeName: System.Object[]

Name               MemberType    Definition
----               ----------    ----------
Count              AliasProperty Count = Length
Address            Method        System.Object& Address(Int32 )
Clone              Method        System.Object Clone()
...

Es posible que este resultado no sea lo que pretende. Pero después de entenderlo, puede usarlo. Por ejemplo, todos los objetos de matriz tienen una propiedad Count . Puede usarlo para contar el número de procesos que se ejecutan en el equipo.

Por ejemplo,

(Get-Process).count

Es importante recordar que los objetos enviados a la canalización se entregan de uno en uno.

Uso de comandos nativos en la canalización

PowerShell permite incluir comandos externos nativos en la canalización. Sin embargo, es importante tener en cuenta que la canalización de PowerShell está orientada a objetos y no admite datos de bytes sin procesar.

Canalización o redirección de salida desde un programa nativo que genera datos de bytes sin procesar convierte la salida en cadenas de .NET. Esta conversión puede provocar daños en la salida de datos sin procesar.

Sin embargo, PowerShell 7.4 agregó la PSNativeCommandPreserveBytePipe característica experimental que conserva los datos de secuencia de bytes al redirigir la secuencia stdout de un comando nativo a un archivo o al canalización de datos de secuencia de bytes a la secuencia stdin de un comando nativo.

Por ejemplo, con el comando nativo curl puede descargar un archivo binario y guardarlo en el disco mediante el redireccionamiento.

$uri = 'https://github.com/PowerShell/PowerShell/releases/download/v7.3.4/powershell-7.3.4-linux-arm64.tar.gz'

# native command redirected to a file
curl -s -L $uri > powershell.tar.gz

También puede canalizar los datos de secuencia de bytes a la secuencia stdin de otro comando nativo. En el ejemplo siguiente se descarga un archivo TAR comprimido mediante curl. Los datos de archivo descargados se transmiten al comando tar para extraer el contenido del archivo.

# native command output piped to a native command
curl -s -L $uri | tar -xzvf - -C .

También puede canalizar la salida de flujo de bytes de un comando de PowerShell a la entrada del comando nativo. En los ejemplos siguientes se usa Invoke-WebRequest para descargar el mismo archivo TAR que el ejemplo anterior.

# byte stream piped to a native command
(Invoke-WebRequest $uri).Content | tar -xzvf - -C .

# bytes piped to a native command (all at once as byte[])
,(Invoke-WebRequest $uri).Content | tar -xzvf - -C .

Esta característica no admite datos de secuencia de bytes al redirigir la salida stderr a stdout. Al combinar las secuencias stderr y stdout, las secuencias combinadas se tratan como datos de cadena.

Investigación de errores de canalización

Cuando PowerShell no puede asociar los objetos canalados a un parámetro del cmdlet receptor, se produce un error en el comando.

En el ejemplo siguiente, intentamos mover una entrada del Registro de una clave del Registro a otra. El Get-Item cmdlet obtiene la ruta de acceso de destino, que luego se canaliza al Move-ItemProperty cmdlet. El Move-ItemProperty comando especifica la ruta de acceso actual y el nombre de la entrada del Registro que se va a mover.

Get-Item -Path HKLM:\Software\MyCompany\sales |
Move-ItemProperty -Path HKLM:\Software\MyCompany\design -Name product

El comando produce un error y PowerShell muestra el siguiente mensaje de error:

Move-ItemProperty : The input object can't be bound to any parameters for
the command either because the command doesn't take pipeline input or the
input and its properties do not match any of the parameters that take
pipeline input.
At line:1 char:23
+ $a | Move-ItemProperty <<<<  -Path HKLM:\Software\MyCompany\design -Name p

Para investigarlo, use el cmdlet para realizar un Trace-Command seguimiento del componente de enlace de parámetros de PowerShell. En el ejemplo siguiente se realiza un seguimiento del enlace de parámetros mientras se ejecuta la canalización. El parámetro PSHost muestra los resultados del seguimiento en la consola y el parámetro FilePath envían los resultados de debug.txt seguimiento al archivo para una referencia posterior.

Trace-Command -Name ParameterBinding -PSHost -FilePath debug.txt -Expression {
  Get-Item -Path HKLM:\Software\MyCompany\sales |
    Move-ItemProperty -Path HKLM:\Software\MyCompany\design -Name product
}

Los resultados del seguimiento son largos, pero muestran los valores que se enlazan al Get-Item cmdlet y, a continuación, los valores con nombre que se enlazan al Move-ItemProperty cmdlet.

...
BIND NAMED cmd line args [`Move-ItemProperty`]
BIND arg [HKLM:\Software\MyCompany\design] to parameter [Path]
...
BIND arg [product] to parameter [Name]
...
BIND POSITIONAL cmd line args [`Move-ItemProperty`]
...

Por último, muestra que el intento de enlazar la ruta de acceso al parámetro Destination de Move-ItemProperty error.

...
BIND PIPELINE object to parameters: [`Move-ItemProperty`]
PIPELINE object TYPE = [Microsoft.Win32.RegistryKey]
RESTORING pipeline parameter's original values
Parameter [Destination] PIPELINE INPUT ValueFromPipelineByPropertyName NO
COERCION
Parameter [Credential] PIPELINE INPUT ValueFromPipelineByPropertyName NO
COERCION
...

Use el Get-Help cmdlet para ver los atributos del parámetro Destination .

Get-Help Move-ItemProperty -Parameter Destination

-Destination <String>
    Specifies the path to the destination location.

    Required?                    true
    Position?                    1
    Default value                None
    Accept pipeline input?       True (ByPropertyName)
    Accept wildcard characters?  false

Los resultados muestran que Destination toma la entrada de canalización solo "por nombre de propiedad". Por lo tanto, el objeto canalado debe tener una propiedad denominada Destination.

Use Get-Member para ver las propiedades del objeto procedente de Get-Item.

Get-Item -Path HKLM:\Software\MyCompany\sales | Get-Member

La salida muestra que el elemento es un objeto Microsoft.Win32.RegistryKey que no tiene una propiedad Destination . Esto explica por qué se produjo un error en el comando.

El parámetro Path acepta la entrada de canalización por nombre o por valor.

Get-Help Move-ItemProperty -Parameter Path

-Path <String[]>
    Specifies the path to the current location of the property. Wildcard
    characters are permitted.

    Required?                    true
    Position?                    0
    Default value                None
    Accept pipeline input?       True (ByPropertyName, ByValue)
    Accept wildcard characters?  true

Para corregir el comando, debemos especificar el destino en el Move-ItemProperty cmdlet y usar Get-Item para obtener la ruta de acceso del elemento que queremos mover.

Por ejemplo,

Get-Item -Path HKLM:\Software\MyCompany\design |
Move-ItemProperty -Destination HKLM:\Software\MyCompany\sales -Name product

Continuación intrínseca de línea

Como ya se ha descrito, una canalización es una serie de comandos conectados por operadores de canalización (|), normalmente escritos en una sola línea. Sin embargo, para mejorar la legibilidad, PowerShell permite dividir la canalización entre varias líneas. Cuando un operador de canalización es el último token de la línea, el analizador de PowerShell combina la siguiente línea al comando actual para continuar con la construcción de la canalización.

Por ejemplo, la siguiente canalización de una sola línea:

Command-1 | Command-2 | Command-3

se puede escribir como:

Command-1 |
    Command-2 |
    Command-3

Los espacios iniciales en las líneas posteriores no son significativos. La sangría mejora la legibilidad.

PowerShell 7 agrega compatibilidad con la continuación de las canalizaciones con el carácter de canalización al principio de una línea. En los ejemplos siguientes se muestra cómo puede usar esta nueva funcionalidad.

# Wrapping with a pipe at the beginning of a line (no backtick required)
Get-Process | Where-Object CPU | Where-Object Path
    | Get-Item | Where-Object FullName -match "AppData"
    | Sort-Object FullName -Unique

# Wrapping with a pipe on a line by itself
Get-Process | Where-Object CPU | Where-Object Path
    |
    Get-Item | Where-Object FullName -match "AppData"
    |
    Sort-Object FullName -Unique

Importante

Cuando se trabaja de forma interactiva en el shell, pegar código con canalizaciones al principio de una línea solo cuando se usa Ctrl+V para pegar. Haga clic con el botón derecho en las operaciones de pegado inserte las líneas una a la vez. Dado que la línea no termina con un carácter de canalización, PowerShell considera que la entrada está completa y ejecuta esa línea como se ha escrito.

Consulte también