Primer script y validación de parámetros
Abstract
En esta lección damos el salto de ejecutar comandos aislados a construir un script
reutilizable con parámetros y validación. Empezaremos diferenciando cmdlets, funciones y scripts, y luego crearemos un generador de README.md que ilustra buenas prácticas: entrada tipada, mensajes de diagnóstico
activables con y salida limpia que puede
encadenarse por el pipeline.
-Verbose -Verbose
Verás cómo aplicar validaciones declarativas para “fallar pronto”, y cómo usar
, here-strings y el operador
Join-Path Join-Path para generar texto robusto. Al finalizar, tendrás un
patrón de script listo para extender y aplicar en proyectos reales.
-f -f
Cmdlets, funciones y scripts
Antes de escribir nuestro primer script, vale la pena aclarar qué entendemos por cmdlet y cómo se relaciona con las funciones y los scripts de PowerShell. Aunque los tres ejecutan acciones en la terminal, difieren en su nivel de integración y propósito dentro del ecosistema.
Cmdlets
Un cmdlet (command-let) es la unidad básica de ejecución en
PowerShell. Cada cmdlet implementa una acción bien definida (por ejemplo,
o
Get-Process Get-Process ) y devuelve objetos .NET al pipeline,
lo que permite encadenarlos fácilmente.
Set-Content Set-Content
Funciones
Una función es un bloque de código definido dentro de PowerShell que
encapsula una tarea específica. Puede comportarse como un cmdlet si se le añade el
atributo
, lo que habilita características
avanzadas como el manejo de
[CmdletBinding()] [CmdletBinding()] , -Verbose -Verbose o
-ErrorAction -ErrorAction .
-WhatIf -WhatIf
Las funciones son ideales para reutilizar lógica sin necesidad de crear archivos
externos. Además, pueden agruparse en módulos (.psm1) para
distribuirlas y cargarlas de forma controlada.
Scripts
Un script es un archivo .ps1 que contiene una secuencia de
comandos, expresiones y funciones. Al igual que las funciones, puede incluir
para comportarse como un cmdlet, pero
se ejecuta desde archivo en lugar de estar cargado en memoria.
[CmdletBinding()] [CmdletBinding()]
Los scripts son útiles para automatizar flujos de trabajo completos, y suelen actuar como punto de entrada para tareas complejas o repetitivas. Su estructura puede incluir parámetros, validaciones y salida tipada, igual que los cmdlets.
Nota
Tu primer script: generar un README.md básico
Hasta ahora hemos trabajado ejecutando comandos directamente en la terminal. El
siguiente paso es encapsular esa lógica en un script sencillo y reutilizable,
guardado dentro de dibs/scripts. Este ejemplo genera el contenido inicial
de un README.md para un proyecto —un caso común que demuestra cómo automatizar tareas repetitivas de manera reproducible y documentada.
Incluye parámetros obligatorios y una opción de verbosity para mostrar mensajes adicionales durante la ejecución. Te recomiendo editar y mantener estos scripts en un entorno cómodo como VS Code, que ofrece resaltado de sintaxis, integración con terminal y depuración básica.
README.md con PowerShell #Requires -Version 7.0
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string] $Name
)
Write-Verbose "Creating README.md for project '$Name'"
@'
# {0}
Project initialized on {1}.
Learn more about READMEs at https://www.makeareadme.com/.
'@ -f $Name, (Get-Date -Format 'yyyy-MM-dd HH:mm:ss') Detalles clave
-
: garantiza que el script se ejecute en PowerShell 7 o superior. 1#Requires -Version 7.0#Requires -Version 7.0 -
convierte el script en un cmdlet, lo que permite usar parámetros como[CmdletBinding()][CmdletBinding()]para mostrar mensajes de diagnóstico controlados con-Verbose-Verbose.Write-VerboseWrite-Verbose
Esto hace que tanto scripts como funciones puedan comportarse como cmdlets, con soporte para common parameters y confirmaciones interactivas. - El bloque
define los parámetros del script. La sintaxisparam(...)param(...)permite aplicar metadatos y validaciones:[Atributo1][Atributo2][Tipo] $Parametro[Atributo1][Atributo2][Tipo] $Parametro-
: indica que el parámetro debe proporcionarse obligatoriamente al ejecutar el script.[Parameter(Mandatory)][Parameter(Mandatory)] -
: evita que se pasen valores vacíos o[ValidateNotNullOrEmpty()][ValidateNotNullOrEmpty()].$null$null -
: especifica el tipo del parámetro, mejorando la validación y el autocompletado.[string][string]
-
- Se usa un here-string para devolver texto multilínea. La sintaxis con
preserva los saltos de línea.@' ... '@@' ... '@ - El operador
: es el format operator de PowerShell. Reemplaza los marcadores-f-f,{0}{0}, ... por los valores que se pasan a continuación, en orden. En este caso,{1}{1}y la fecha formateada se insertan en el texto del README.$Name$Name - En PowerShell, la última expresión evaluada en un script o función se devuelve automáticamente como salida, incluso sin usar la palabra clave
.returnreturn
Aquí, el here-string formateado es la última expresión, por lo que se devuelve como resultado del script. Este comportamiento sigue la filosofía de diseño de PowerShell: todo es un objeto y todo puede fluir por el pipeline.
Formato de cadenas en PowerShell: guía rápida
En los ejemplos del curso usaremos cadenas literales ( ) junto con composite formatting ('...' '...' ). Esta
combinación es uniforme, legible y predecible, especialmente útil al dividir
líneas largas o mantener estilos de salida consistentes.
-f -f
- Composite formatting: usa el operador
o el método-f-fpara reemplazar marcadores[string]::Format()[string]::Format(),{0}{0}, etc., por los argumentos que se pasan en orden.{1}{1}Formato compuesto'Hello, {0}! Today is {1:yyyy-MM-dd}.' -f $User, (Get-Date) # o bien [string]::Format('Hello, {0}! Today is {1:yyyy-MM-dd}.', $User, (Get-Date))Ventaja principal: separa claramente la plantilla del contenido, facilitando la lectura y el mantenimiento.
- Interpolación: las comillas dobles (
) expanden variables ("...""...") y expresiones ($var$var), como en el siguiente ejemplo:$(...)$(...)Interpolación de cadenas"Hello, $User! Today is $(Get-Date -Format 'yyyy-MM-dd')."Es más concisa y natural en scripts pequeños, pero puede ser menos práctica en textos largos o con muchas inserciones, donde el formato compuesto ofrece más control.
Sobre return return en PowerShell
return return
Si bien en PowerShell existe, no usaremos la palabra clave
en este curso. Es redundante en la mayoría de los scripts, ya que toda expresión
evaluada produce salida automáticamente. De hecho, usar return return puede provocar resultados inesperados 2 en pipelines o cuando se combinan funciones y scripts.
return return
Su único uso obligatorio es dentro de métodos de clases para devolver valores de forma explícita. Dado que no trabajaremos con clases en este curso, puedes ignorarlo completamente.
Get-Help about_Return -Online -Online para consultar la documentación en
línea.
Ejecutar el script y guardar el resultado
dibs/scripts $content = .\core\New-Readme.ps1 -Name 'Utility Scripts - DIBS' -Verbose
Set-Content -Value $content -Path README.md -Encoding UTF8 Detalles clave
-
ejecuta el script desde la carpeta actual (.\core\New-Readme.ps1 -Name '...'.\core\New-Readme.ps1 -Name '...'dibs/scripts). -
activa mensajes de diagnóstico generados por-Verbose-Verbose. Aunque no definimos el parámetroWrite-VerboseWrite-Verboseexplícitamente, se agrega automáticamente gracias a-Verbose-Verbose.[CmdletBinding()][CmdletBinding()] - La salida del script (el texto del README) se asigna a una variable mediante
. Esto permite inspeccionarla o modificarla antes de escribirla en disco.$content = ...$content = ... -
crea o sobrescribeSet-Content -Value $content -Path README.md -Encoding UTF8Set-Content -Value $content -Path README.md -Encoding UTF8README.mdcon codificación UTF-8. Es buena práctica incluir siemprepara evitar inconsistencias entre sistemas.-Encoding UTF8-Encoding UTF8
Validación básica de parámetros
PowerShell permite validar los valores de los parámetros directamente en su definición. Esto evita errores comunes y mantiene los scripts más seguros y predecibles sin necesidad de escribir lógica adicional.
param(
# No vacío o nulo
[ValidateNotNullOrEmpty()]
[string] $NotNullOrEmptyString,
# Solo valores permitidos
[ValidateSet('debug','info','warn','error')]
[string] $OnlyTheseLevels = 'info',
# Rango numérico válido
[ValidateRange(1, 10)]
[int] $NumberInRange = 5,
# Coincidir un patrón (minúsculas, dígitos y guiones)
[ValidatePattern('^[a-z0-9-]{3,30}$')]
[string] $MatchesPattern = 'valid-slug',
# Validación personalizada con script block
[ValidateScript({ $_ % 2 -eq 0 })]
[int] $EvenNumber = 2
) Detalles clave
-
— evita que se pasen valores vacíos o nulos.[ValidateNotNullOrEmpty()][ValidateNotNullOrEmpty()] -
— limita el valor a una lista predefinida de opciones.[ValidateSet(...)][ValidateSet(...)] -
— asegura que el valor esté dentro del rango indicado.[ValidateRange(min, max)][ValidateRange(min, max)] -
— valida el formato del valor usando una expresión regular.[ValidatePattern('regex')][ValidatePattern('regex')] -
— ejecuta un bloque de script por cada valor recibido. Dentro del bloque,[ValidateScript({ ... })][ValidateScript({ ... })]representa el valor actual del parámetro que se está validando.$_$_
Si el bloque devuelve, la validación pasa; si devuelve$true$trueo lanza un error, la validación falla.$false$false
Por ejemplo,solo acepta números pares. Este tipo de validación es útil para comprobaciones personalizadas, como verificar que un archivo exista o que una cadena cumpla reglas específicas.{ $_ % 2 -eq 0 }{ $_ % 2 -eq 0 }
Tip
Las validaciones permiten “fallar pronto”: si un valor no cumple con las reglas, PowerShell detiene la ejecución y muestra un mensaje claro sin ejecutar el resto del script.
Ejercicio: Test-Readme.ps1
Requisitos
Implementa un script de validación que verifique un README.md mínimo en
el directorio indicado. Debe devolver si pasa
todas las validaciones y
$true $true en caso contrario (sin usar $false $false ).
return return
- Parámetros:
-
(carpeta a validar).[Parameter(Mandatory)][Validation1(...)][Validation2(...)][string] $Path[Parameter(Mandatory)][Validation1(...)][Validation2(...)][string] $Path
La ruta debe:- no ser nula ni vacía y
- existir y contener un archivo
README.md.
-
-
(mensajes de diagnóstico).[switch] $Verbose[switch] $Verbose
-
- Validaciones mínimas sobre
README.mden:$Path$Path- Existe el archivo
README.md. - Contiene un título H1 en la primera línea:
.# Nombre# Nombre - Contiene la frase
(línea generada por el script de la lección).Project initialized onProject initialized on
-
Uso esperado
./path/to/Test-Readme.ps1 -Path '.' -Verbose.) no contiene un README.md
entonces el script falla por validación de parámetros. Si existe pero no cumple
las reglas, devuelve $false $false . Si todo está correcto,
devuelve $true $true .
Hints
- Usa
para construir rutas.Join-Path $Path 'README.md'Join-Path $Path 'README.md' -
habilita mensajes emitidos con-Verbose-Verbose; el parámetro se expone automáticamente al usarWrite-VerboseWrite-Verboseen el script.[CmdletBinding()][CmdletBinding()]
Cmdlets útiles
# ¿Es un directorio?
Test-Path -Path "." -PathType Container
# ¿Existe un archivo?
Test-Path -Path "README.md" -PathType Leaf
# Leer todo el contenido como string
Get-Content -Path "README.md" -Raw
# Verificar coincidencias
"# Título" -match '^#\s+.+' # H1 en la primera línea
"Project initialized on ..." -match 'Project initialized on'
# Operadores lógicos
$true -and !$false Solución
#Requires -Version 7.0
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[ValidateScript({ Test-Path (Join-Path $_ 'README.md') -PathType Leaf })]
[string] $Path
)
$readmePath = Join-Path $Path 'README.md'
Write-Verbose ('Checking for README.md at {0}' -f $readmePath)
$content = Get-Content -Path $readmePath -Raw
$hasH1 = $content -match '^#\s+.+'
$hasMarker = $content -match 'Project initialized on'
if ($hasH1 -and $hasMarker) {
Write-Verbose 'README.md looks good.'
$true
}
else {
Write-Verbose 'README.md does not follow expected format.'
$false
}Conclusiones
En esta lección pasaste de ejecutar comandos sueltos a estructurar automatizaciones con un script claro y validado. Diferenciaste cmdlets, funciones y scripts; construiste un generador deREADME.md; y
aplicaste validaciones de parámetros para prevenir errores desde el inicio.
Además, incorporaste diagnósticos con -Verbose -Verbose (expuesto por
[CmdletBinding()] [CmdletBinding()] ) y buenas prácticas como Join-Path Join-Path , here-strings y el operador de formato -f -f .
Puntos clave
- Cmdlets, funciones y scripts: misma filosofía, distintos contenedores y niveles de integración.
- Primer script útil: generar contenido de
README.mdcomo plantilla reproducible. - Validación declarativa:
,[ValidateNotNullOrEmpty()][ValidateNotNullOrEmpty()],[ValidateSet(...)][ValidateSet(...)],[ValidateRange(...)][ValidateRange(...)]y[ValidatePattern(...)][ValidatePattern(...)]para “fallar pronto”.[ValidateScript(...)][ValidateScript(...)] - Diagnóstico integrado:
expone[CmdletBinding()][CmdletBinding()]; usa-Verbose-Verbosepara trazas controladas.Write-VerboseWrite-Verbose - Rutas portables: preferir
frente a concatenar separadores.Join-PathJoin-Path - Salida de texto robusta: combinar here-strings con
para separar plantilla y datos.-f-f
¿Qué nos llevamos?
La productividad en PowerShell surge de pequeños scripts bien diseñados:
reciben parámetros válidos, explican lo que hacen con y devuelven resultados previsibles. Cada verificación que agregas al inicio te
ahorra depurar al final.
-Verbose -Verbose
Adopta una mentalidad de automatización incremental: comienza por una tarea concreta (p. ej., generar un README), formaliza sus entradas con validaciones y compártela como base para el siguiente paso. Así conviertes acciones puntuales en herramientas reutilizables que crecen contigo y con tu equipo.
Comparativas y otros ecosistemas
¿Con ganas de más?
Referencias recomendadas
- “Improving your parameterized script” (pp. 249–258) en Learn PowerShell in a month of lunches, fourth edition: Covers Windows, Linux, and MacOS por Travis PlunkCapítulo práctico sobre cómo llevar un script parametrizado de PowerShell “de básico a sólido”. Parte desde un ejemplo funcional (
) con ayuda embebida y dos parámetros, y muestra por qué conviene emitir objetos conGet-DiskInventoryGet-DiskInventoryen lugar de salida formateada, permitiendo usarSelect-ObjectSelect-Objecto exportar a CSV sin cambios. ConFormat-TableFormat-Tablese vuelve “avanzado” y habilita[CmdletBinding()][CmdletBinding()]para trazas activables. También introduce mejoras en la definición de parámetros: obligatorios con-Verbose-Verbose, alias con[Parameter(Mandatory=$true)][Parameter(Mandatory=$true)]y validaciones con[Alias('host')][Alias('host')]. Aunque centrado en Windows, los principios son transferibles. El capítulo introduce conceptos más avanzados que se explicarán más adelante o que quedan fuera del alcance de este curso, pero resulta ideal para quienes buscan crear scripts en PowerShell que sean profesionales, reutilizables y claros.[ValidateSet(...)][ValidateSet(...)]