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