Pipes en PowerShell o el arte de encadenar comandos

Este es uno de los capítulos del tutorial PowerShell. Encontrarás los enlaces a todos los de capítulos, al final de este artículo.

Si has hecho el tutorial sobre el terminal, en particular en el capítulo sobre redirigir entrada y salida en Linux te explico esto de las tuberías o encadenar comandos. Esta característica también la puedes encontrar en PowerShell. Esta herramienta es realmente interesante porque te ahorra pasos intermedios. Y es que esto de encadenar comandos consiste, simplemente, en pasar la salida o resultado de un comando, como entrada del siguiente comando.

De esta forma, utilizando pipes, o mejor dicho, encadenando comandos, no necesitamos utilizar archivos intermedios donde guardar la información correspondiente a la salida de un comando, simplemente ese resultado lo pasamos al siguiente comando.

Así, este capítulo está dedicado al uso de los pipes o encadenamiento de comandos.

Pipes en PowerShell o encadenar comandos

Pipes en PowerShell o el arte de encadenar comandos

¿Pipes?

Seguramente te estés preguntando, porque a esto de encadenar comandos se le llama pipes o pipelines. Esto es así por el carácter que se utiliza para realizar esta operación |, que tiene pinta de eso. Como te comentaba en la introducción esto no es nada exclusivo de PowerShell, sino que lo puedes encontrar en muchos otros intérpretes de comandos, como en el propio Bash.

¿En que consiste los Pipes en PowerShell?

Te tengo que confesar que esto ya te lo adelanté en el capítulo anterior del tutorial sobre la ayuda en PowerShell, en el que te mostraba información sobre un comando concreto. En concreto lo utilicé en,

Get-Command Get-Help | Get-Member

Si te fijas en la instrucción anterior, existen dos partes diferencias, una a la izquierda del pipe y otro a la derecha. En la parte izquierda con Get-Command Get-Help, obtienes un listado de todos los cmdlet, alias, funciones que tienes instalado en tu equipo, mientras que con la parte derecha Get-Member, obtienes los miembros del objeto, es decir, sus [propiedades y métodos]()

Pero, ¿porque no utilizar directamente Get-Member Get-Help? Inténtalo y verás que el resultado que obtienes es algo como lo que te muestro a continuación,

PS /home/lorenzo> Get-Member Get-Help
Get-Member: You must specify an object for the Get-Member cmdlet.

Si, el cmdlet Get-Member necesita que le pases como parámetro de entrada un objeto no un texto.

Como ves, esto de encadenar comandos, resulta muy útil, sin embargo, puede convertir, un script en algo completamente ilegible, cuando encadenas varios comandos. Sin embargo, tienes la opción de después de cada | hacer un salto de línea, ya que PowerShell, lo interpretará todo como una única instrucción. La siguiente instrucción es equivalente a la que puse anteriormente,

Get-Command Get-Help |
Get-Member

¿Puedo encadenar cualquier comando?

No. No todos los comandos se pueden encadenar. Y, ¿como puedo saber si se puede encadenar un comando?. Para esto tienes que utilizar la ayuda. En concreto tienes que utilizar la opción -Full y fijarte en las secciones INPUTS y OUTPUTS donde te indica si admite la posibilidad de encadenar y que tipos de datos admite.

Por ejemplo, si utilizas la ayuda con el cmdlet del portapapeles verás que no admite ninguna entrada,

PS> Get-Help -Full Get-Clipboard

INPUTS
    None
OUTPUTS
    System.String

En el caso de que lo utilices con el cmdlet que te permite poner cosas en portapapeles, verás lo siguiente,

PS> Get-Help -Full Set-Clipboard

INPUTS
    System.String[]
OUTPUTS
    System.Object

Sin embargo, aunque el cmdlet Set-Clipboard admite tanto entrada como salida, no lo admite en cualquier circunstancia. También te tienes que fijar en lo que indica en cada uno de los parámetros que admite el cmdlet. Si te fijas, para cada uno de los parámetros indica una serie de características. En concreto para el parámetro -Append del cmdlet Set-Clipboard tienes los siguientes parámetros,

-Append

    Required?                    false
    Position?                    Named
    Accept pipeline input?       false
    Parameter set name           (All)
    Aliases                      None
    Dynamic?                     false
    Accept wildcard characters?  false

Aquí, el parámetro en el que te tienes que fijar en Accept pipeline input?, que en este caso está establecido a false. Sin embargo, en el caso del parámetro -Value, te encontrarás lo siguiente,

Accept pipeline input?       true (ByValue, ByPropertyName)

Por valor y por nombre de propiedad

El parámetro -Value, no solo admite encadenar comandos sino que te permite hacerlo por valor, ByValue y por propiedad ByPropertyName. ¿A que se refiere por valor y por propiedad? Para aclara este punto ejecuta la siguiente instrucción,

PS> Get-Help Stop-Process -Full

Te tienes que fijar en la sección PARAMETERS en concreto en -Name, cuyo resultado es el siguiente,

-Name <string[]>

   Required?                    true
   Position?                    Named
   Accept pipeline input?       true (ByPropertyName)
   Parameter set name           Name
   Aliases                      ProcessName
   Dynamic?                     false
   Accept wildcard characters?  false

Admite que le pasemos objetos con la propiedad -Name, a eso se refiere por nombre de propiedad, es decir, que tiene que coincidir por nombre de propiedad con el nombre del parámetro del cmdlet.

Pasando parámetros por nombre de propiedad

Vamos a crear unos objetos de forma sencilla, para comprender con mas facilidad este aspecto. Para hacerlo tienes que crear un archivo, que puede llamarse procesos.csv que tenga el siguiente contenido,

Name
sleep
nada

Una vez creado el archivo procesos.csv el siguiente paso es importarlo. Para esto tienes que utilizar el cmdlet Import-Csv, tal y como te muestro a continuación,

PS> Import-Csv procesos.csv

Name
----
sleep
nada

Ahora tienes dos objetos con la propiedad Name, sleep y nada. Para comprobar este aspecto, de nuevo, tienes que recurrir a Get-Member y ejecutar la siguiente instrucción,

PS> Import-Csv ./processes.csv | Get-Member

   TypeName: System.Management.Automation.PSCustomObject

Name        MemberType   Definition
----        ----------   ----------
Equals      Method       bool Equals(System.Object obj)
GetHashCode Method       int GetHashCode()
GetType     Method       type GetType()
ToString    Method       string ToString()
Name        NoteProperty string Name=sleep

Ahí tienes la propiedad Name. Ahora en otro emulador de terminal ejecuta el comando sleep 9999 &. Lo puedes ejecutar varias veces, si quieres matar varios procesos de un comando. Si quieres ver los procesos que tienes en marcha con el nombre sleep, ejecuta de nuevo,

PS> Get-Process -Name sleep

 NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
 ------    -----      -----     ------      --  -- -----------
      0     0,00       0,50       0,00   18837 …27 sleep
      0     0,00       0,50       0,00   18859 …27 sleep
      0     0,00       0,57       0,00   18882 …27 sleep
      0     0,00       0,50       0,00   18905 …27 sleep

Ahora fíjate, vamos a pasar el archivo csv procesos.csv al cmdlet Stop-Process. Vamos a detener el proceso sleep, por nombre de propiedad, porque coincide el nombre de la propiedad con el nombre del parámetro del cmdlet,

PS> Import-Csv procesos.csv | Stop-Process

Stop-Process: Cannot find a process with the name "nada". Verify the process name and call the cmdlet again.

Si, te ha arrojado un error, pero es lógico, porque tal y como indica el mensaje, no ha podido encontrar ningún proceso con el nombre nada. Sin embargo, si ejecutas,

PS> Get-Process -Name sleep

Get-Process: Cannot find a process with the name "sleep". Verify the process name and call the cmdlet again.

En este caso el error que te arroja es porque no ha encontrado ningún proceso que tenga el nombre sleep. Lo cual es totalmente lógico, porque los mataste con la instrucción anterior.

Pasando por valor

En el caso del cmdlet Set-Clipboard el parámetro -Value, admite tanto por nombre de propiedad, es decir que el objeto que le pasemos tiene que tener la propiedad -Value, o por valor, que en este caso tiene que ser una cadena de texto. Así, tienes las dos opciones. Directamente enviar los valores,

PS> Write-Output 'valor1' 'valor2' | Set-Clipboard

valor1
valor2

O con el método que has utilizado anteriormente, creando un archivo valores.txt como el siguiente,

Value
valor1
valor2

Y en este caso la operativa sería la siguiente,

PS> Import-Csv ./valores.txt | Set-Clipboard
PS> Get-Clipboard
valor1
valor2

¿Y ante la duda?

Cuando encadenamos dos comandos y el comando de la derecha admite tanto por valor como por propiedad ¿que prevalece? Siempre prevalece por valor. Solo en el caso de que pro valor falle se intenta por nombre de propiedad.

Conclusiones

Como ves esto de encadenar comandos en PowerShell es de gran utilidad, y te permitirá ahorrar pasos inútiles intermedios.


Imagen de portada de Samuel Sianipar en Unsplash

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *