DEV Community

Cover image for Una forma fácil de pasar parámetros en tus scripts de PowerShell
Coding Bugs
Coding Bugs

Posted on

Una forma fácil de pasar parámetros en tus scripts de PowerShell

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:\
Enter fullscreen mode Exit fullscreen mode

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:\
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

¿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
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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 variable CmdParams 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
}
Enter fullscreen mode Exit fullscreen mode

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)