Mi apodo en las redes es Coding Bugs y llevo desde el año 2004 trabajando como desarrollador y arquitecto de software. En mi dashboard encontrarás artículos con información sobre lenguajes de programación, técnicas de programación y otros conceptos que te ayudarán a mejorar.
Ya sabéis que PowerShell es el lenguaje de programación que Microsoft pone a nuestra disposición para la "automatización de tareas multiplataforma compuesta por un shell de línea de comandos, un lenguaje de scripting y un marco de gestión de la configuración".
En este artículo mostraré una manera diferente de proporcionar valores a los parámetros de comandos y scripts. Esta es una forma sencilla y simple de declarar parámetros y asignar sus valores que os permitirá organizar vuestros scripts de forma que todo tenga más sentido.
Forma estándar
Si conoces PowerShell ya sabes como proporcionar parámetros en tus llamadas. Si no lo conoces, simplemente, indica el nombre del parámetro antecedido por un guion y, a continuación, escribe su valor.
$ Get-ChildItem -Path C:\ -Filter *.txt
# Devuelve todos los ficheros de texto existentes en C:\
Como puedes observar, la forma estándar es muy fácil y para casos en los que hagas invocaciones inmediatas es la mejor manera de proporcionar parámetros.
Sin embargo, imagina que tienes que contar el número de ficheros de un determinado tipo ubicados en una localización específica de tu máquina. En este caso, tendríamos que repetir tantas veces como fuera necesario la invocación al comando anterior, Get-ChildItem
, proporcionando el parámetro Filter
en base al tipo de fichero solicitado.
$ Get-ChildItem -Path C:\ -Filter *.html | Measure | Select Count
# Devuelve todos los ficheros HTML existentes en C:\
$ Get-ChildItem -Path C:\ -Filter *.css | Measure | Select Count
# Devuelve todos los ficheros CSS existentes en C:\
$ Get-ChildItem -Path C:\ -Filter *.js | Measure | Select Count
# Devuelve todos los ficheros JavaScript existentes en C:\
Para ahorrarnos tiempo cada vez que nos soliciten esa información vamos a generar un script propio en el que añadamos estas llamadas. Además, vamos a incorporar un parámetro en el script para especificar la ubicación concreta donde contar cada uno de los ficheros HTML, CSS y JS que hay.
param(
[Parameter(Mandatory=$true)]
[String] $Location
)
Return @{
Html = $(Get-ChildItem -Path $Location -Filter *.html | Measure).Count
Css = $(Get-ChildItem -Path $Location -Filter *.css | Measure).Count
Js = $(Get-ChildItem -Path $Location -Filter *.js | Measure).Count
}
¿Qué sucede si ahora nos piden incluir más y más tipos de fichero con diferentes extensiones? La respuesta sería incluir más y más líneas a nuestro script para cumplir con lo que nos solicitan. Pero, ¿y si hubiera otra forma más sencilla y más versatil dotando a nuestro script de la capacidad de abstraerse de los tipos de extensión?
Una hashtable
para pasar parámetros
Empezaremos por añadir un nuevo parámetro donde se indiquen las extensiones de los ficheros que tenemos que contar. Ahora sólo tendremos que iterar sobre estos valores y pasar la extensión correspondiente al comando Get-ChildItem
.
param(
[Parameter(Mandatory=$true)]
[String] $Location,
[Parameter(Mandatory=$true)]
[String[]] $Extensions
)
Return $Extensions
| Foreach {
$CurrentExtension = $_.ToLower()
# Transformar la extensión a minúsculas
Return @{ $_ = $(Get-ChildItem -Path $Location -Filter $CurrentExtension | Measure).Count }
# Devuelve un objeto con la extensión como clave y el recuento obtenido como valor
}
Con esto sería suficiente para nuestro objetivo pero, como programador que somos, queremos que sea aún más abstracto y nos permita mantener la llamada al comando Get-ChildItem
más limpia, evitando tener que volver a modificarla con nuevos parámetros.
Al ser una línea de comandos un poco compleja es mejor evitar su modificación al máximo posible o hacerla más simple diviéndola en trozos más simples. Por el bien de este artículo, elegimos la primera opción.
PowerShell permite el paso de un objeto hashtable
para proporcionar cada uno de los parámetros que necesitamos del comando a ejecutar. En nuestro caso, el comando es Get-ChildItem
y los parámetros que estamos utilizando son Path
y Filter
.
# La variable hashtable que crearemos será como la siguiente
$CmdParams = @{
Path = "."
Filter = "*.*"
}
# Y la llamada al comando es como la siguiente, importante el uso de "@" en el paso de nuestro hashtable
Get-ChildItem @CmdParams
Igual que en el caso anterior, tenemos que actualizar nuestra variable hashtable
con la extensión correspondiente en cada iteración del Foreach
.
param(
[Parameter(Mandatory=$true)]
[String] $Location,
[Parameter(Mandatory=$true)]
[String[]] $Extensions
)
$CmdParams = @{
Path = $Location
Filter = "*.*"
}
Return $Extensions
| Foreach {
$CurrentExtension = $_.ToLower()
# Transformar la extensión a minúsculas
$CmdParams.Filter = "*.$CurrentExtension"
# Establecer el filtro en la extensión actual
Return @{ $_ = $(Get-ChildItem @CmdParams | Measure).Count }
# Devuelve un objeto con la extensión como clave y el recuento obtenido como valor
}
Ya hemos conseguido lo que buscabamos, hemos abstraido la llamada al comando Get-ChildItem
con respecto a los parámetros que necesitamos pasarle. De esta forma, podemos modificar la variable CmdParams
añadiendo, cambiando o quitando cualquier par clave-valor en base a las necesidades funcionales que tengamos.
Podemos ver aquí el segundo principio del paradigma SOLID, Open Closed Principle, en el que se indica que el código debe ser cerrado a cualquier modificación pero abierto para extender su funcionalidad. Podemos decir que el propósito de la iteración sobre cada una de las extensiones es retornar el número de ficheros existentes en la ubicación indicada. El comando
Get-ChildItem
puede ser invocado con otros parámetros que modificará el número de ficheros que retorne pero su resultado seguirá siendo el mismo (cerrado a modificación), por tanto, nuestra variableCmdParams
permite que los ficheros a devolver pueda ser diferente dependiendo de los parámetros que reciba (abierto a extensión).
Imagina que nos solicitan otra nueva funcionalidad. Ahora se necesita contabilizar los ficheros de la ubicación proporcionada y los ficheros de las carpetas que se encuentran inmediatamente dentro, es decir, el primer nivel.
Déjame explicarte que el comando Get-ChildItem
dispone de parámetros que nos permiten implementar este tipo de requisitos sin necesidad de hacer ningún tipo de ingeniería avanzada, usaremos los parámetros Recurse
para establecer que se realice la búsqueda en la localización indicada y en todas las que hay por debajo y, Depth
para establecer el máximo número de recursividad que necesatimos, en nuestro caso, el valor es de 1.
Seguro que ya has visualizado el cambio que hay que realizar al código anterior. Desde mi punto de vista, algo muy sencillo que no nos llevará más de 5 minutos entre el cambio y las pruebas para verificar que es correcto.
En nuestro código tenemos que incluir los dos parámetros anteriores en la variable CmdParams
para que se aplique. Es un cambio limpio, sólo añadir nuevos elementos en nuestro hashtable
sin tener que modificar la línea de comandos donde se encuentra el comando que, como hemos dicho, es ciertamente compleja.
param(
[Parameter(Mandatory=$true)]
[String] $Location,
[Parameter(Mandatory=$true)]
[String[]] $Extensions
)
$CmdParams = @{
Path = $Location
Filter = "*.*"
Recurse = $true
Depth = 1
}
Return $Extensions
| Foreach {
$CurrentExtension = $_.ToLower()
# Transformar la extensión a minúsculas
$CmdParams.Filter = "*.$CurrentExtension"
# Establecer el filtro en la extensión actual
Return @{ $_ = $(Get-ChildItem @CmdParams | Measure).Count }
# Devuelve un objeto con la extensión como clave y el recuento obtenido como valor
}
Resumiendo
El paso de parámetros en comandos y/o scripts en PowerShelles sencillo. Podemos utilizar la forma estándar para llamadas a comandos o en scripts sencillos. Y, también, podemos usar técnicas más avanzadas que nos permitan tener un código más limpio e implementar modificaciones de forma más sencilla.
El uso de hashtables
es una forma fácil de visualizar los valores de los parámetros de los comandos que invocamos. Además, estructura nuestro código haciendo que sea más legible para los demás o, incluso, para nosotros mismos despues de pasado un tiempo.
El código de este artículo lo tenéis en este gist.
Feedback
Si te ha gustado este artículo no olvides clicar en el corazón o el unicornio y compartirlo con tus amigos y compañeros. Déjame tus comentarios en la parte inferior sobre lo que te ha gustado o como mejorar y si quieres que escriba acerca de otros temas mándame un mensaje directo a mi cuenta de twitter.
Créditos
La imagen de cabecera procede de Unsplash y es cortesía de Markus Spiske
Top comments (0)