Programación · n.º 9 entre los lenguajes más usados

Aprende PowerShell desde cero

Automatiza Windows y la nube con comandos que trabajan con objetos.

  • 15 lecciones
  • 19 ejercicios interactivos
  • 17 conceptos clave
  • 24,6 % lo usa*

Qué es PowerShell y para qué sirve

PowerShell es la shell y el lenguaje de scripts de Microsoft. A diferencia de otras terminales, sus comandos (cmdlets) se pasan objetos con propiedades, no texto, lo que hace muy fácil filtrar, ordenar o exportar información. Windows trae Windows PowerShell 5.1, y PowerShell 7 se instala aparte y también funciona en macOS y Linux.

  • Administrar y automatizar Windows
  • Gestionar servidores y la nube (Azure, Microsoft 365)
  • Tareas repetitivas con archivos

Para empezar: Busca «PowerShell» en el menú Inicio o abre Terminal de Windows y escribe los ejemplos.

Nivel 1 Básico

Lección 1 · Básico

Qué es PowerShell y tu primer cmdlet

Los comandos de PowerShell se llaman cmdlets y siguen el patrón Verbo-Sustantivo: Get-Date, Get-Process, Stop-Service… Si conoces el verbo y el objeto, casi puedes adivinar el nombre.

Write-Output (o simplemente escribir un valor) muestra algo en la consola. PowerShell no distingue mayúsculas de minúsculas en los nombres de los comandos.

Ejemplo práctico
Get-Date
Write-Output "Hola desde PowerShell"
2 + 3          # 5
Get-Process | Select-Object -First 3

Ejercicio

¿Qué forma tienen los nombres de los cmdlets?

Ver la solución

Respuesta: Verbo-Sustantivo. Todos siguen el patrón Verbo-Sustantivo, como Get-Date o Remove-Item.

Lección 2 · Básico

Descubrir comandos: Get-Help, Get-Command y Get-Member

No hace falta memorizar: Get-Command busca comandos (admite comodines), Get-Help explica cómo se usa uno y sus ejemplos, y Get-Member muestra qué propiedades y métodos tiene un objeto.

La primera vez, Update-Help (como administrador) descarga la ayuda completa.

Ejemplo práctico
Get-Command *service*
Get-Help Get-Service -Examples
Get-Service | Get-Member

Ejercicio

Completa para ver las propiedades de los objetos que devuelve Get-Process.

Get-Process | Get-
Ver la solución

Respuesta: Member. Get-Member lista las propiedades y métodos de los objetos que recibe.

Lección 3 · Básico

Variables y texto

Las variables empiezan por $: $nombre = "Ana". No hace falta declarar el tipo, aunque puedes forzarlo con corchetes, como [int].

Entre comillas dobles, las variables se sustituyen por su valor; entre comillas simples, el texto se queda tal cual.

Ejemplo práctico
$nombre = "Ana"
[int]$edad = "30"     # se convierte a número
$edad + 1             # 31

"Hola, $nombre"       # Hola, Ana
'Hola, $nombre'       # Hola, $nombre
"El año que viene: $($edad + 1)"

Ejercicio

¿Qué muestra 'Hola, $nombre' (comillas simples)?

Ver la solución

Respuesta: Hola, $nombre. Las comillas simples no sustituyen variables: el texto se muestra literalmente.

Encuentra los errores

Este script debería mostrar «Hola, Ana» y «El año que viene tendrás 31 años». Tiene 2 errores con las comillas.

Ver la solución
  • Línea 3: Entre comillas simples PowerShell no sustituye las variables: mostraría $nombre tal cual. Con comillas dobles sí. Corrección: Write-Host "Hola, $nombre"
  • Línea 4: Dentro de un texto solo se sustituye la variable: saldría «30 + 1». Para calcular una expresión dentro del texto se usa $( ). Corrección: Write-Host "El año que viene tendrás $($edad + 1) años"

Conceptos clave

Lección 4 · Básico

La tubería de objetos

La barra | pasa la salida de un cmdlet al siguiente. En PowerShell lo que viaja son objetos completos, así que puedes ordenar por una propiedad o elegir columnas sin recortar texto.

Sort-Object ordena, Select-Object elige propiedades o los primeros resultados y Format-Table presenta el resultado como tabla.

Ejemplo práctico
# Los 5 procesos que más memoria usan
Get-Process |
  Sort-Object WorkingSet -Descending |
  Select-Object -First 5 Name, Id, WorkingSet

Ejercicio

¿Qué pasa por la tubería | en PowerShell?

Ver la solución

Respuesta: Objetos con propiedades. Cada cmdlet recibe objetos, por eso Sort-Object WorkingSet puede ordenar por esa propiedad directamente.

Nivel 2 Intermedio

Lección 5 · Intermedio

Filtrar con Where-Object

Where-Object se queda con los objetos que cumplen una condición escrita entre llaves; $_ representa el objeto actual.

Los operadores de comparación son palabras: -eq (igual), -ne, -gt (mayor que), -lt, -like (con comodines) y -match (expresión regular).

Ejemplo práctico
# Archivos de más de 100 MB en Descargas
Get-ChildItem "$HOME\Downloads" -File |
  Where-Object { $_.Length -gt 100MB } |
  Select-Object Name, @{ n = "MB"; e = { [math]::Round($_.Length / 1MB) } }

# Servicios detenidos
Get-Service | Where-Object Status -eq "Stopped"

Ejercicio

Completa el operador «mayor que».

Where-Object { $_.Length  1GB }
Ver la solución

Respuesta: -gt. En PowerShell, «mayor que» se escribe -gt (greater than); > sirve para redirigir a un archivo.

Escribe el código

Escribe una sola línea que obtenga los procesos con Get-Process, se quede con los que usan más de 100 MB de memoria (propiedad WorkingSet64) y los ordene de mayor a menor uso.

Tab escribe una sangría; para salir del editor con el teclado, pulsa Esc y luego Tab. Ctrl + Intro corrige.

Ver la solución
Get-Process | Where-Object { $_.WorkingSet64 -gt 100MB } | Sort-Object WorkingSet64 -Descending

Cada | pasa objetos al siguiente cmdlet: Where-Object se queda con los que cumplen la condición ($_ es cada proceso) y Sort-Object los ordena por la propiedad que indiques.

Lección 6 · Intermedio

Condiciones y bucles

if, elseif y else funcionan con los mismos operadores (-eq, -gt…). foreach recorre una colección y ForEach-Object hace lo mismo dentro de una tubería.

Los bloques van entre llaves.

Ejemplo práctico
$espacioLibreGB = (Get-PSDrive C).Free / 1GB

if ($espacioLibreGB -lt 20) {
  "Queda poco espacio: $([math]::Round($espacioLibreGB)) GB"
} else {
  "Espacio suficiente"
}

foreach ($n in 1..3) { "Vuelta $n" }
1..3 | ForEach-Object { $_ * 10 }   # 10, 20, 30

Ejercicio

¿Qué produce 1..3?

Ver la solución

Respuesta: Los números 1, 2 y 3. El operador de rango .. genera la lista de enteros entre los dos extremos.

Escribe el código

Escribe un bucle foreach que recorra los números del 1 al 20 (1..20) y muestre con Write-Output solo los múltiplos de 3.

Tab escribe una sangría; para salir del editor con el teclado, pulsa Esc y luego Tab. Ctrl + Intro corrige.

Ver la solución
foreach ($n in 1..20) {
    if ($n % 3 -eq 0) {
        Write-Output $n
    }
}

1..20 crea la secuencia de números y % da el resto de la división. Los operadores de comparación de PowerShell son palabras: -eq, -ne, -gt, -lt…

Lección 7 · Intermedio

Scripts y la directiva de ejecución

Un script es un archivo .ps1 con comandos. Se ejecuta escribiendo su ruta, como .\limpiar.ps1.

Por seguridad, Windows bloquea los scripts según la directiva de ejecución. Get-ExecutionPolicy muestra la actual; para tus propios scripts basta con RemoteSigned solo para tu usuario. No desactives la protección del todo.

Ejemplo práctico
Get-ExecutionPolicy -List

# Permite tus scripts locales (los descargados deben estar firmados)
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

# Ejecutar un script de la carpeta actual
.\limpiar.ps1

Ejercicio

¿Qué extensión tienen los scripts de PowerShell?

Ver la solución

Respuesta: .ps1. Los scripts de PowerShell se guardan con la extensión .ps1.

Conceptos clave

Nivel 3 Avanzado

Lección 8 · Avanzado

Funciones con parámetros

Una función agrupa código con un nombre, que conviene que siga también el patrón Verbo-Sustantivo. El bloque param() declara sus parámetros, con tipo y valor por defecto.

Con [Parameter(Mandatory)] PowerShell pide el valor si no lo pasas.

Ejemplo práctico
function Get-PrecioFinal {
  param(
    [Parameter(Mandatory)][decimal]$Precio,
    [decimal]$Iva = 21
  )
  [math]::Round($Precio * (1 + $Iva / 100), 2)
}

Get-PrecioFinal -Precio 100          # 121,00 (con Windows en español)
Get-PrecioFinal -Precio 100 -Iva 10  # 110,0

Ejercicio

Completa el bloque que declara los parámetros.

function Saludar {
  ([string]$Nombre)
  "Hola, $Nombre"
}
Ver la solución

Respuesta: param. param(...) declara los parámetros de una función o de un script.

Encuentra los errores

Este script debería sumar 2 y 3 y mostrar «Correcto» solo si el resultado es 5. Tiene 2 errores típicos de quien viene de otros lenguajes: uno hace que la suma falle y otro, que «Correcto» se muestre siempre.

Ver la solución
  • Línea 6: En PowerShell las funciones se llaman sin paréntesis ni comas: (2, 3) es una lista que se pasa entera a $A y no se puede convertir a [int]. Corrección: $total = Sumar -A 2 -B 3
  • Línea 7: = asigna, así que la condición siempre se cumple. Para comparar se usa -eq. Corrección: if ($total -eq 5) { Write-Host "Correcto" }

Conceptos clave

Lección 9 · Avanzado

Errores con try/catch

Muchos errores de los cmdlets no detienen el comando: solo muestran un aviso en rojo. Para poder capturarlos con try y catch añade -ErrorAction Stop.

Dentro de catch, $_ es el error y $_.Exception.Message su mensaje.

Ejemplo práctico
try {
  Get-Item "C:\no-existe.txt" -ErrorAction Stop
}
catch {
  Write-Warning "No se pudo leer: $($_.Exception.Message)"
}
finally {
  "Comprobación terminada"
}

Ejercicio

¿Para qué sirve -ErrorAction Stop?

Ver la solución

Respuesta: Para que el error se pueda capturar con catch. Convierte el error en uno que detiene la ejecución, y así try/catch lo captura.

Lección 10 · Avanzado

Automatizar: exportar, comprobar y programar

Como trabajas con objetos, exportar a CSV o JSON es una línea: Export-Csv y ConvertTo-Json. Get-FileHash calcula la huella de un archivo para comprobar que no ha cambiado.

Para que un script se ejecute solo, prográmalo con el Programador de tareas de Windows, indicando como programa powershell.exe -File ruta\script.ps1.

Ejemplo práctico
# Inventario de programas en un CSV que abre Excel
Get-Process | Select-Object Name, Id, CPU |
  Export-Csv "$HOME\procesos.csv" -NoTypeInformation -Encoding UTF8

# Huella SHA-256 de un archivo descargado
Get-FileHash "$HOME\Downloads\instalador.exe" -Algorithm SHA256

Ejercicio

Completa el cmdlet que guarda los objetos en un archivo CSV.

Get-Service | -Csv servicios.csv
Ver la solución

Respuesta: Export. Export-Csv convierte cada objeto en una fila del archivo CSV.

Lección 11 · Avanzado

Hashtables y objetos personalizados

Una hashtable guarda pares clave-valor entre @{ }: $config = @{ Ruta = "C:\datos"; Dias = 7 }. Se lee con $config.Ruta o $config["Ruta"], y ContainsKey comprueba si existe una clave.

[PSCustomObject]@{ … } crea un objeto con esas propiedades. A diferencia de una hashtable, se muestra como una tabla y funciona con Sort-Object, Export-Csv o ConvertTo-Json, así que es la forma habitual de devolver resultados propios.

Ejemplo práctico
$precios = @{ Teclado = 35; Raton = 12.5 }
$precios["Monitor"] = 189          # añadir una clave
Write-Output $precios.Teclado      # 35
Write-Output $precios.ContainsKey("Impresora")   # False

$equipos = foreach ($nombre in "PC-01", "PC-02") {
    [PSCustomObject]@{
        Equipo = $nombre
        Discos = 2
        Revisado = (Get-Date -Year 2026 -Month 5 -Day 4).ToString("dd/MM/yyyy")
    }
}
$equipos | Format-Table
# Equipo Discos Revisado
# ------ ------ --------
# PC-01       2 04/05/2026
# PC-02       2 04/05/2026

Ejercicio

¿Qué ventaja tiene [PSCustomObject]@{ … } frente a una hashtable @{ … } para devolver resultados?

Ver la solución

Respuesta: Se comporta como un objeto: se muestra en tabla y funciona con Sort-Object, Export-Csv o ConvertTo-Json. Los cmdlets de la tubería trabajan con las propiedades de los objetos; una hashtable es un único objeto con claves dentro.

Lección 12 · Avanzado

CSV y JSON

Import-Csv convierte cada fila de un CSV en un objeto con una propiedad por columna, y Export-Csv hace lo contrario. Así puedes filtrar y ordenar una hoja de cálculo exportada con los mismos cmdlets de siempre.

ConvertTo-Json y ConvertFrom-Json traducen objetos a JSON y al revés, el formato de casi todas las API y de muchos archivos de configuración. Usa -Depth si el objeto tiene niveles anidados.

Ejemplo práctico
@"
nombre,departamento,horas
Ana,Ventas,38
Luis,Soporte,42
Marta,Ventas,45
"@ | Set-Content empleados.csv

$empleados = Import-Csv empleados.csv
$empleados | Where-Object { [int]$_.horas -gt 40 } | Select-Object nombre, horas
# nombre horas
# ------ -----
# Luis   42
# Marta  45

$resumen = [PSCustomObject]@{ Total = $empleados.Count; Ventas = ($empleados | Where-Object departamento -eq "Ventas").Count }
$resumen | ConvertTo-Json -Compress     # {"Total":3,"Ventas":2}

Ejercicio

Completa para leer un archivo CSV como objetos.

$datos = -Csv "ventas.csv"
Ver la solución

Respuesta: Import. Import-Csv lee el CSV y crea un objeto por fila; Export-Csv guarda objetos en un CSV.

Lección 13 · Avanzado

Expresiones regulares: -match y -replace

-match comprueba si un texto cumple una expresión regular y, si es así, guarda lo encontrado en la variable automática $Matches. Los grupos con nombre, como (?<anio>\d{4}), se leen después con $Matches.anio.

-replace sustituye todo lo que encaja con el patrón, y Select-String busca un patrón en archivos, como grep. Por defecto no distinguen mayúsculas; las versiones con c delante (-cmatch, -creplace) sí.

Ejemplo práctico
$linea = "2026-05-04 10:05 ERROR No se encuentra config.yml"

if ($linea -match '^(?<fecha>\d{4}-\d{2}-\d{2}) (?<hora>\d{2}:\d{2}) (?<nivel>\w+)') {
    Write-Output "Nivel $($Matches.nivel) el $($Matches.fecha) a las $($Matches.hora)"
}
# Nivel ERROR el 2026-05-04 a las 10:05

"600 123 456" -replace '\s', ''          # 600123456
"ERROR" -match "error"                  # True (no distingue mayúsculas)
"ERROR" -cmatch "error"                 # False

# Buscar en todos los .log de la carpeta:
# Select-String -Path *.log -Pattern "ERROR"

Escribe el código

Escribe la función Test-CodigoPostal con un parámetro [string]$Codigo que devuelva $true si el código tiene exactamente 5 dígitos y $false en cualquier otro caso. Usa -match con ^ y $.

Tab escribe una sangría; para salir del editor con el teclado, pulsa Esc y luego Tab. Ctrl + Intro corrige.

Ver la solución
function Test-CodigoPostal {
    param([string]$Codigo)
    return $Codigo -match '^\d{5}$'
}

'^\d{5}$' exige que el texto entero sean cinco dígitos. Usa comillas simples para que PowerShell no intente sustituir $ como si fuera una variable.

Lección 14 · Avanzado

Archivos y carpetas sin sustos

Get-ChildItem lista archivos (con -Recurse, también los de las subcarpetas; con -Filter *.pdf, solo los PDF) y devuelve objetos con su tamaño (Length) y su fecha (LastWriteTime). Test-Path comprueba si una ruta existe.

Los cmdlets que cambian cosas (Copy-Item, Move-Item, Remove-Item) aceptan -WhatIf, que muestra lo que harían sin hacerlo. Pruébalo siempre antes de borrar o mover en bloque.

Ejemplo práctico
$origen = "$HOME\Descargas"

# Los 5 archivos más grandes de Descargas, en MB
Get-ChildItem $origen -File -Recurse |
    Sort-Object Length -Descending |
    Select-Object -First 5 Name, @{ Name = "MB"; Expression = { [math]::Round($_.Length / 1MB, 1) } }

# PDF de hace más de 90 días: primero ver qué pasaría…
$viejos = Get-ChildItem $origen -Filter *.pdf | Where-Object LastWriteTime -lt (Get-Date).AddDays(-90)
$destino = "$HOME\Documentos\PDF antiguos"
if (-not (Test-Path $destino)) { New-Item -ItemType Directory -Path $destino | Out-Null }
$viejos | Move-Item -Destination $destino -WhatIf
# …y, si es lo que esperabas, repetir la última orden sin -WhatIf

Ejercicio

¿Qué hace Remove-Item *.tmp -WhatIf?

Ver la solución

Respuesta: Muestra qué archivos borraría, sin borrar nada. -WhatIf simula la orden: lista lo que haría sin hacerlo. -Confirm es la opción que pide confirmación para cada elemento.

Lección 15 · Avanzado

Conectarse a una API web

Invoke-RestMethod hace una petición HTTP y, si la respuesta es JSON, la convierte directamente en objetos: puedes filtrarlos y ordenarlos como cualquier otro resultado de PowerShell.

Para enviar datos se usa -Method Post con -Body (normalmente un objeto convertido con ConvertTo-Json) y -ContentType "application/json". Guarda las claves de acceso en variables de entorno, nunca escritas en el script.

Ejemplo práctico
# Consultar una API pública (GitHub) y quedarse con algunos datos
$repo = Invoke-RestMethod -Uri "https://api.github.com/repos/PowerShell/PowerShell"
$repo | Select-Object full_name, stargazers_count, license

# Enviar datos a un servicio (ejemplo con una dirección ficticia)
$cuerpo = @{ titulo = "Copia de seguridad terminada"; ok = $true } | ConvertTo-Json
$cabeceras = @{ Authorization = "Bearer $env:MI_TOKEN" }
Invoke-RestMethod -Uri "https://api.ejemplo.es/avisos" -Method Post -Body $cuerpo -ContentType "application/json" -Headers $cabeceras

Ejercicio

¿Dónde conviene guardar la clave (token) de una API que usa tu script?

Ver la solución

Respuesta: En una variable de entorno, como $env:MI_TOKEN. Si la clave está escrita en el script, cualquiera que vea o comparta el archivo la tiene. Una variable de entorno la mantiene fuera del código.

¿Has terminado? Sigue practicando

Repasa los conceptos en el diccionario, practica con estas herramientas o prueba otro lenguaje: las ideas que has aprendido (variables, condiciones, funciones, objetos…) te servirán en cualquiera.

Proyectos para practicar PowerShell

  • Seguridad y privacidad

    Comprobar el hash de un archivo

    Calcula el SHA-256, SHA-1 o MD5 de un archivo y compáralo con la suma publicada.

  • Texto y programación

    Comparar dos textos

    Encuentra las diferencias entre dos versiones de un texto, línea a línea y palabra a palabra.

Fuentes consultadas

* Porcentaje de personas que dijeron haber usado PowerShell en la Stack Overflow Developer Survey 2026: Technology (pregunta sobre lenguajes de programación, de scripts y de marcado; todas las respuestas). El progreso de las lecciones se guarda solo en este navegador.