about_Pipelines

Se aplica a: Windows PowerShell 2.0, Windows PowerShell 3.0

TEMA

about_pipelines

DESCRIPCIÓN BREVE

Combinación de comandos en canalizaciones en Windows 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.

Puede usar las canalizaciones para enviar los objetos que genera un comando a fin de usarlos como entrada para otro comando para su procesamiento. Además, puede enviar la salida de ese comando a otro comando. El resultado es una cadena de comandos o "canalización" muy eficaz que se compone de una serie de comandos sencillos.

Por ejemplo,

Command-1 | Command-2 | Command-3  

En este ejemplo, los objetos que emite Command-1 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. Como no hay más comandos en la canalización, se muestran los resultados en la consola.

En una canalización, los comandos se procesan de izquierda a derecha en el orden en que aparecen. El procesamiento se administra como una única operación y se muestra la salida en cuanto se genera.

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

         get-process notepad | stop-process

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

A continuación se muestra un ejemplo práctico. Esta canalización de comandos obtiene los archivos de texto del directorio actual, selecciona únicamente los archivos que tienen una longitud superior a los 10.000 bytes, los ordena por longitud y muestra en una tabla el nombre y la longitud de cada uno de ellos.

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

Esta canalización está formada por cuatro comandos en el orden especificado. El comando se escribe horizontalmente, pero vamos a mostrar el proceso verticalmente en el siguiente gráfico.

       Get-ChildItem -path *.txt

                  |
                  |   (FileInfo objects )
                  |   (    .txt         )
                  |
                  V                   

       Where-Object {$_.length -gt 10000}

                  |
                  |   (FileInfo objects )
                  |   (    .txt         )
                  |   ( Length > 10000  )
                  |
                  V

       Sort-Object -property Length

                  |
                  |   (FileInfo objects  )
                  |   (    .txt          )
                  |   ( Length > 10000   )
                  |   ( Sorted by length )
                  |
                  V

       Format-Table -property name, length

                  |   
                  |   (FileInfo objects     )
                  |   (    .txt             )
                  |   ( Length > 10000      )
                  |   ( Sorted by length    )
                  |   (Formatted in a table )
                  |
                  V
        Name                       Length
        ----                       ------
        tmp1.txt                    82920
        tmp2.txt                   114000
        tmp3.txt                   114000

USO DE LAS CANALIZACIONES

Los cmdlets de Windows PowerShell están diseñados para usarse en canalizaciones. Por ejemplo, normalmente puede canalizar los resultados de un cmdlet Get a un cmdlet de acción (por ejemplo, Set, Start, Stop o Rename) para el mismo nombre.

Por ejemplo, puede canalizar los servicios del cmdlet Get-Service a los cmdlets Start-Service o Stop-Service (aunque los servicios deshabilitados no se pueden reiniciar de este modo).

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

get-service wmi | start-service

Los cmdlets que obtienen y establecen los objetos de los proveedores de Windows PowerShell, como los cmdlets Item y ItemProperty, también están diseñados para emplearse en las canalizaciones.

Por ejemplo, puede canalizar los resultados de un comando Get-Item o Get-ChildItem del proveedor del Registro de Windows PowerShell al cmdlet New-ItemProperty. Este comando agrega una nueva entrada de 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-Object y Measure-Object) se usan de forma casi exclusiva en las canalizaciones. Puede canalizar cualquier objeto a estos cmdlets.

Por ejemplo, puede canalizar todos los procesos del equipo al comando Sort-Object y ordenarlos por el número de controladores del proceso.

get-process | sort-object -property handles

Además, puede canalizar cualquier objeto a los cmdlets de formato (como Format-List y Format-Table), a los cmdlets Export (como Export-Clixml y Export-CSV) y a los cmdlets Out (como Out-Printer).

Por ejemplo, puede canalizar el proceso Winlogon al cmdlet Format-List para mostrar en una lista todas las propiedades del proceso.

get-process winlogon | format-list -property *

Con un poco de práctica verá que combinando unos sencillos comandos en las canalizaciones ahorra tiempo, escribe menos y la creación de scripts resulta más eficaz.

FUNCIONAMIENTO DE LAS CANALIZACIONES

Al "canalizar" objetos, es decir, al enviar los objetos de la salida de un comando a otro comando, Windows PowerShell trata de asociar los objetos canalizados con uno de los parámetros del cmdlet receptor.

Para ello, el componente de "enlace de parámetros" de Windows PowerShell, que asocia objetos de entrada con parámetros de cmdlets, busca un parámetro que cumpla los siguientes criterios:

-- El parámetro debe aceptar la entrada de una canalización (no todos lo hacen)

-- El parámetro debe aceptar el tipo de objeto que se envía o un tipo en el que el objeto se pueda convertir.

-- El parámetro no puede estar ya en uso en el comando.

Por ejemplo, el cmdlet Start-Service tiene muchos parámetros, pero solo dos, InputObject y Name, aceptan la entrada de la canalización. El parámetro Name toma las cadenas y el parámetro InputObject, objetos de servicio. Por lo tanto, puede canalizar cadenas y objetos de servicio (y objetos con propiedades que se puedan convertir en objetos de cadenas y servicios) al cmdlet Start-Service.

Si el componente de enlace de parámetro de Windows PowerShell no puede asociar los objetos canalizados con un parámetro del cmdlet receptor, se produce un error en el comando y Windows PowerShell le solicita los valores de parámetro que faltan.

No puede forzar el componente de enlace de parámetro para que asocie los objetos canalizados con un parámetro determinado (tampoco puede sugerir ningún parámetro). En su lugar, la lógica del componente administra la canalización de la forma más eficaz posible.

PROCESAMIENTO UNO POR UNO

Canalizar objetos a un comando se parece al uso de un parámetro del comando para enviar los objetos.

Por ejemplo, canalizar objetos que representan los servicios del equipo a un comando Format-Table, como:

  get-service | format-table -property name, dependentservices

se parece a la operación de guardar los objetos de servicio en una variable y usar el parámetro InputObject de Format-Table para enviar el objeto de servicio.

  $services = get-service
                  format-table -inputobject $services -property name, dependentservices

o a la operación de incrustar el comando en el valor de parámetro

                  format-table -inputobject (get-service wmi) -property name, dependentservices

Sin embargo, hay una diferencia importante. Cuando se canalizan varios objetos a un comando, Windows PowerShell envía los objetos al comando uno por uno. Al usar un parámetro de comando, los objetos se envían como un objeto de matriz.

Esta diferencia, aparentemente técnica, puede tener consecuencias interesantes y a menudo útiles.

Por ejemplo, si canaliza varios objetos de proceso desde el cmdlet Get-Process al cmdlet Get-Member, Windows PowerShell envía cada objeto de proceso, uno por uno, a Get-Member. Get-Member muestra la clase (tipo) .NET de los objetos de proceso, así como sus propiedades y métodos (Get-Member elimina los duplicados; de este modo, si los objetos son todos del mismo tipo, solo muestra un tipo de objeto).

En este caso, Get-Member muestra las propiedades y los métodos de cada objeto de proceso, es decir, un objeto System.Diagnostics.Process.

                 get-process | get-member

                    TypeName: System.Diagnostics.Process

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

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

                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 el que pretendía, pero puede aplicarlo en cuanto lo comprenda. Por ejemplo, una matriz de objetos de proceso tiene una propiedad Count que se puede usar para contar el número de procesos del equipo.

(get-process).count

Esta distinción puede ser importante, así que recuerde que, al canalizar objetos a un cmdlet, estos se entregan uno por uno.

ACEPTA LA ENTRADA DE LA CANALIZACIÓN

Para poder recibir objetos en una canalización, el cmdlet receptor debe tener un parámetro que acepte la entrada de la canalización. Puede usar un comando Get-Help con los parámetros Full o Parameter para determinar cuál de los parámetros de un cmdlet (si los hay) acepta la entrada de la canalización.

En la visualización predeterminada de Get-Help, el elemento "Acepta la entrada de la canalización" aparece en una tabla de atributos de parámetro. Esta tabla solo se muestra cuando se usan los parámetros Full o Parameter del cmdlet Get-Help.

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

        get-help start-service -full

        get-help start-service -parameter *

Por ejemplo, la ayuda del cmdlet Start-Service muestra que los parámetros InputObject y Name aceptan la entrada de la canalización ("true"). Todos los demás parámetros tienen el valor "false" en la fila "¿Aceptar canalización?".

        -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?                    1
           Default value
      -->  Accept pipeline input?       true (ByValue, ByPropertyName)
           Accept wildcard characters?  true

        -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?                    false
           Position?                    named
           Default value
      -->  Accept pipeline input?       true (ByValue)
           Accept wildcard characters?  false

Esto significa que puede enviar objetos (PsObjects) a través de la canalización al cmdlet Where-Object y Windows PowerShell asociará el objeto con el parámetro InputObject.

MÉTODOS PARA ACEPTAR LA ENTRADA DE LA CANALIZACIÓN

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

-- ByValue: Los parámetros que aceptan la entrada "por valor" pueden aceptar los objetos canalizados que tengan el mismo tipo .NET que su valor de parámetro o que los objetos que se pueden convertir a ese tipo.

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

-- ByPropertyName: Los parámetros que aceptan la entrada "por nombre de propiedad" pueden aceptar los objetos canalizados solamente cuando una propiedad del objeto tiene el mismo nombre que el parámetro.

Por ejemplo, el parámetro Name del cmdlet Start-Service puede aceptar los objetos que tienen una propiedad Name

(para obtener una lista de las propiedades de un objeto, canalícelo a Get-Member).

Algunos parámetros pueden aceptar objetos por valor o por nombre de propiedad. Estos parámetros están diseñados para tomar fácilmente la entrada de la canalización.

INVESTIGAR LOS ERRORES DE LA CANALIZACIÓN

Si se produce un error en un comando debido a un error de canalización, puede investigarlo y volver a escribir el comando.

Por ejemplo, el siguiente comando intenta mover una entrada del registro desde una clave del registro a otra usando el cmdlet Get-Item para obtener la ruta de acceso de destino y canalizándola al cmdlet Move-ItemProperty.

De forma específica, el comando usa el cmdlet Get-Item para obtener la ruta de acceso de destino. Usa un operador de canalización para enviar el resultado al cmdlet Move-ItemProperty. El comando Move-ItemProperty especifica el nombre y la ruta de acceso actual 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

Se produce un error en el comando y Windows PowerShell muestra el siguiente mensaje de error:

         Move-ItemProperty : The input object cannot be bound to any parameters for the
         command either because the command does not 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 product

Para investigarlo, use el cmdlet Trace-Command para hacer un seguimiento del componente Parameter Binding de Windows PowerShell. El siguiente comando hace un seguimiento del componente Parameter Binding mientras se procesa el comando. Usa el parámetro -pshost para mostrar los resultados en la consola y el comando -filepath para enviarlos al archivo debug.txt y tenerlos como referencia.

         trace-command -name parameterbinding -expression {get-item -path hklm:\software\mycompany\sales |
             move-itemproperty -path hklm:\software\mycompany\design -name product} -pshost -filepath debug.txt

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

       ... 
        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 no tuvo éxito.

        ...
        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
        ... 

Para investigar el error, use el cmdlet Get-Help para ver los atributos del parámetro Destination. El siguiente comando obtiene información detallada del parámetro Destination.

get-help move-itemproperty -parameter destination

Los resultados muestran que el parámetro Destination solo toma la entrada de la canalización "por nombre de propiedad". Es decir, el objeto canalizado debe tener una propiedad denominada Destination.

        -destination <string>
            Specifies the path to the destination location.

            Required?                    true
            Position?                    2
            Default value
            Accept pipeline input?       true (ByPropertyName)
            Accept wildcard characters?  true    

Para ver las propiedades del objeto que se va a canalizar al cmdlet Move-ItemProperty, canalícelo al cmdlet Get-Member. El siguiente comando canaliza los resultados de la primera parte del comando al cmdlet Get-Member.

          get-item -path hklm:\software\mycompany\sales | get-member

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

Para corregir el comando, debemos especificar el destino en el cmdlet Move-ItemProperty. Podemos usar un comando Get-ItemProperty para obtener la ruta de acceso, pero el nombre y el destino deben especificarse en la parte Move-ItemProperty del comando.

         get-item -path hklm:\software\mycompany\design | 
             move-itemproperty -dest hklm:\software\mycompany\design -name product    

Para comprobar que el comando ha funcionado, use un comando Get-ItemProperty:

get-itemproperty hklm:\software\mycompany\sales

Los resultados muestran que la entrada del registro Product se movió a la clave Sales.

        PSPath       : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\software\mycompany\sales
        PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\software\mycompany
        PSChildName  : sales
        PSDrive      : HKLM
        PSProvider   : Microsoft.PowerShell.Core\Registry
        Product      : 18

VEA TAMBIÉN

about_objects

about_parameters

about_command_syntax

about_foreach