Lección 5 de 512 min
Bash y PowerShell: automatizar la terminal
Comandos, tuberías, variables y scripts en Bash, y el pipeline de objetos de PowerShell.
Cada vez que despliegas, haces una copia de seguridad o limpias los registros viejos de un servidor, repites los mismos pasos en la terminal. Si los escribes en un script, se ejecutan igual cada vez, los puede lanzar un pipeline de CI y quedan documentados en el repositorio.
En Linux y macOS manda Bash, o shells muy parecidos como Zsh, el que trae macOS por defecto. En Windows manda PowerShell, que hoy también funciona en Linux y macOS. Los dos resuelven lo mismo con filosofías distintas: Bash conecta programas que se pasan texto; PowerShell conecta comandos que se pasan objetos.
Bash: comandos, tuberías y redirecciones
Comandos, argumentos y tuberías
Programas pequeños que se encadenan con | para resolver tareas grandes.
- Significa:
- Bourne Again SHell
- Desde:
- 1989
Una línea de Bash es un comando seguido de argumentos separados por espacios: ls -l /var/log ejecuta el programa ls con la opción -l y la ruta /var/log. Las opciones cortas llevan un guion (-l) y las largas, dos (--all). Con man ls lees el manual de cada programa.
La tubería | conecta la salida de un comando con la entrada del siguiente. Es la filosofía Unix: herramientas que hacen una sola cosa bien (grep filtra líneas, sort ordena, uniq -c cuenta repetidos, head se queda con las primeras) y que combinas entre sí.
Úsalo cuando
Para explorar archivos y registros, y para tareas rápidas en servidores Linux, contenedores y pipelines de CI.
Ten en cuenta
Los espacios separan argumentos: rm mis fotos intenta borrar dos archivos, mis y fotos. Y lo que pasa por la tubería es texto plano, así que cada programa tiene que volver a trocearlo para entenderlo.
# Las 5 IP que más peticiones han hecho al servidor
cut -d ' ' -f 1 access.log | # primera columna: la IP
sort | # agrupa las IP iguales
uniq -c | # cuenta cuántas veces sale cada una
sort -rn | # de más a menos
head -n 5 # solo las 5 primerasRedirecciones: >, >>, 2> y 2>&1
Mandar la salida o los errores de un comando a un archivo en vez de a la pantalla.
- Canales:
- 0 stdin, 1 stdout, 2 stderr
Cada programa tiene tres canales: la entrada estándar (stdin, número 0), la salida estándar (stdout, número 1) y la salida de errores (stderr, número 2). Por defecto, los dos últimos van a tu pantalla, pero puedes redirigirlos.
> envía stdout a un archivo y lo sobrescribe; >> lo añade al final. 2> hace lo mismo con los errores, y 2>&1 envía los errores al mismo sitio que la salida normal. < hace lo contrario: lee la entrada desde un archivo. Y /dev/null es un agujero negro donde tirar lo que no quieres ver.
Úsalo cuando
Para guardar el registro de un script, separar los errores del resultado o silenciar mensajes que no aportan.
Ten en cuenta
El orden importa: comando > salida.log 2>&1 guarda todo en el archivo, pero comando 2>&1 > salida.log deja los errores en la pantalla, porque 2>&1 copia el destino que tenía stdout en ese momento. Y un > donde querías >> borra el contenido anterior.
./backup.sh > backup.log # salida normal al archivo (lo sobrescribe)
./backup.sh >> backup.log # la añade al final
./backup.sh 2> errores.log # solo los errores
./backup.sh > todo.log 2>&1 # salida y errores, juntos
./backup.sh 2> /dev/null # descarta los erroresPruébalo
Abre una terminal (en Windows, desde WSL o Git Bash) y ejecuta ls /etc /no-existe > salida.txt. Verás el error en pantalla y el listado en el archivo. Después añade 2>&1 al final y mira qué cambia.
Bash: variables, condiciones y scripts
Variables, comillas y sustitución de comandos
Guardar valores, decidir cuándo se expanden y capturar la salida de un comando.
Una variable se asigna sin espacios alrededor del =, como en nombre="Ana", y se lee con $: $nombre o ${nombre}. Las variables de entorno, como PATH o HOME, son las que heredan los programas que lanzas; con export conviertes una tuya en una de ellas.
Las comillas cambian lo que hace Bash. Entre comillas dobles ("Hola $nombre") las variables se expanden, pero el texto queda como un solo argumento aunque tenga espacios. Entre comillas simples ('Hola $nombre') todo es literal. Y la sustitución de comandos, $(comando), ejecuta el comando y pone su salida en ese lugar: hoy=$(date +%F).
Úsalo cuando
En cualquier script: para no repetir rutas y nombres, para leer configuración del entorno y para usar el resultado de un comando dentro de otro.
Ten en cuenta
Pon comillas dobles alrededor de tus variables: si $archivo vale mis fotos.jpg, rm $archivo intenta borrar mis y fotos.jpg, mientras que rm "$archivo" borra el correcto. Y nombre = "Ana", con espacios, no asigna nada: Bash busca un comando llamado nombre.
nombre="Ana"
echo "Hola, $nombre" # Hola, Ana
echo 'Hola, $nombre' # Hola, $nombre (literal)
hoy=$(date +%F) # por ejemplo, 2026-11-07
archivo="copia-${hoy}.tar.gz" # las llaves marcan dónde acaba el nombre
tar -czf "$archivo" ./datos
export ENTORNO="produccion" # visible para los programas que lancesCondicionales y bucles
Decidir con if y repetir con for y while, comprobando archivos, textos y números.
En Bash, if no evalúa una expresión: ejecuta un comando y mira si ha terminado bien. Lo más habitual es usar [[ … ]], que comprueba condiciones: -f ruta (existe el archivo), -d ruta (existe la carpeta), -z "$texto" (texto vacío), == para comparar textos y -eq, -lt o -gt para números.
for recorre una lista de palabras o de archivos (for f in *.log) y while repite mientras un comando tenga éxito, por ejemplo, para leer un archivo línea a línea. Los bloques se cierran con fi y done, y case elige entre varios patrones.
Úsalo cuando
Para que un script reaccione a lo que encuentra: crear una carpeta si no existe, procesar todos los archivos de un directorio o reintentar una conexión.
Ten en cuenta
Los espacios dentro de los corchetes son obligatorios: [[-f archivo]] da error. Y dentro de [[ ]], < y > comparan textos por orden alfabético, no números: para números usa -lt y -gt.
destino="./copias"
if [[ ! -d "$destino" ]]; then
mkdir -p "$destino"
fi
for archivo in ./logs/*.log; do
bytes=$(wc -c < "$archivo")
if [[ $bytes -gt 1000000 ]]; then
echo "Comprimiendo $archivo ($bytes bytes)"
gzip "$archivo"
fi
doneScripts: shebang, códigos de salida y set -euo pipefail
Convertir comandos sueltos en un programa que falla de forma segura.
- Éxito:
- Código 0
- Extensión:
- .sh
Un script es un archivo de texto con comandos. Su primera línea, el shebang (#!/usr/bin/env bash), le dice al sistema con qué intérprete ejecutarlo. Le das permiso de ejecución con chmod +x copia.sh y lo lanzas con ./copia.sh; dentro, $1, $2… son los argumentos que recibe.
Cada comando termina con un código de salida: 0 significa éxito y cualquier otro número, error. Lo consultas con $? y lo devuelves con exit 1. Es lo que miran if, && (ejecuta lo siguiente solo si lo anterior fue bien) y || (solo si falló), y también tu pipeline de CI para saber si un paso ha fallado.
Por defecto, Bash sigue adelante aunque un comando falle. Por eso muchos scripts empiezan con set -euo pipefail: -e detiene el script en el primer error, -u trata como error usar una variable sin definir y -o pipefail hace que una tubería falle si falla cualquiera de sus comandos, no solo el último.
Úsalo cuando
En cuanto repites una secuencia de comandos más de dos veces, o cuando la tiene que ejecutar otra persona o un pipeline.
Ten en cuenta
set -e tiene excepciones: no actúa en la condición de un if ni en las cadenas con && o || (salvo en su último comando), así que no sustituye a comprobar los errores importantes. Si un script crece mucho o necesita estructuras de datos, suele ser mejor pasarlo a Python o a Node.js.
#!/usr/bin/env bash
set -euo pipefail
# Uso: ./copia.sh <carpeta>
# Si falta el argumento, sale con error y muestra el mensaje
origen="${1:?Indica la carpeta que quieres copiar}"
destino="copia-$(date +%F).tar.gz"
tar -czf "$destino" "$origen"
echo "Copia creada: $destino"Nota
Pasa tus scripts por ShellCheck: detecta variables sin comillas, condiciones mal escritas y muchos otros errores típicos de Bash antes de que causen daño.
PowerShell: objetos en lugar de texto
PowerShell nació en 2006 para administrar Windows. Su gran idea es que los comandos no se pasan texto, sino objetos de .NET con propiedades: un proceso tiene Name, Id y la memoria que usa, y puedes filtrar por ellas sin recortar nada.
Cmdlets y el pipeline de objetos
Comandos con nombre Verbo-Sustantivo que se pasan objetos, no texto.
- Desde:
- 2006
- Basado en:
- .NET
Los comandos de PowerShell se llaman cmdlets y siguen el patrón Verbo-Sustantivo: Get-Process, Stop-Service, New-Item, Remove-Item. Los verbos salen de una lista aprobada, así que si conoces Get-Service puedes adivinar Start-Service. Get-Command busca comandos y Get-Help explica cómo se usan.
La diferencia clave está en el pipeline: | pasa objetos con propiedades, no líneas de texto. Where-Object filtra por una propiedad, Sort-Object ordena y Select-Object elige columnas, sin trocear texto como harías con cut o awk. Al final, PowerShell muestra los objetos como una tabla.
Úsalo cuando
Para administrar Windows, Active Directory, Microsoft 365 o Azure, y para cualquier automatización donde trabajar con propiedades (fechas, tamaños, estados) sea más cómodo que trocear texto.
Ten en cuenta
Los alias engañan: en Windows, ls es un alias de Get-ChildItem y no entiende opciones de Unix como -la. En los scripts, escribe el nombre completo del cmdlet: se lee mejor y funciona igual en todas partes.
# Los 5 procesos que más memoria usan
Get-Process |
Sort-Object -Property WorkingSet64 -Descending |
Select-Object -First 5 -Property Name, Id, WorkingSet64
# Archivos .log de más de 1 MB sin tocar en 30 días
Get-ChildItem -Path ./logs -Filter *.log |
Where-Object { $_.Length -gt 1MB } |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
Remove-Item -WhatIf # -WhatIf: muestra qué borraría, sin borrarPowerShell 7, scripts .ps1 y política de ejecución
El PowerShell multiplataforma: pwsh en Windows, Linux y macOS.
- Comando:
- pwsh
- Licencia:
- MIT
- Extensión:
- .ps1
Hay dos PowerShell. Windows PowerShell 5.1 viene instalado en Windows, se lanza con powershell.exe y ya solo recibe correcciones. PowerShell 7 es de código abierto, se basa en el .NET moderno, funciona en Windows, Linux y macOS y se lanza con pwsh. Es el que conviene usar en proyectos nuevos y en pipelines de CI.
Los scripts son archivos .ps1. Las variables empiezan por $ ($destino = "./copias"), los parámetros del script se declaran con param() y los errores se capturan con try y catch, como en JavaScript. $ErrorActionPreference = 'Stop' cumple un papel parecido a set -e en Bash.
La política de ejecución decide si Windows permite ejecutar scripts. Con RemoteSigned, los scripts creados en tu equipo funcionan, pero los descargados de internet deben estar firmados. La consultas con Get-ExecutionPolicy y la cambias con Set-ExecutionPolicy. En Linux y macOS no se aplica.
Úsalo cuando
Cuando el equipo mezcla Windows, Linux y macOS y quieres un solo lenguaje de scripts, o cuando automatizas servicios de Microsoft como Azure.
Ten en cuenta
La política de ejecución no es una barrera de seguridad, sino una protección contra ejecutar algo por accidente; aun así, no la pongas en Unrestricted para salir del paso: RemoteSigned para tu usuario suele bastar. Y algunos módulos antiguos solo funcionan en Windows PowerShell 5.1.
#!/usr/bin/env pwsh
# Comprime una carpeta en una copia con la fecha de hoy
param(
[Parameter(Mandatory)]
[string] $Origen
)
$ErrorActionPreference = 'Stop' # detente ante cualquier error
$destino = "copia-$(Get-Date -Format 'yyyy-MM-dd').zip"
try {
Compress-Archive -Path $Origen -DestinationPath $destino
Write-Host "Copia creada: $destino"
} catch {
Write-Warning "No se pudo crear la copia: $_"
exit 1
}¿Sabías que…?
No tienes que elegir para siempre: en Windows, WSL te da un Linux con Bash completo, y en Linux puedes instalar pwsh. Muchos sistemas de CI, como GitHub Actions, te dejan elegir el shell de cada paso; lo verás en el módulo de CI/CD.
Interactivo
Bash y PowerShell cara a cara
Activa o desactiva opciones para compararlas cara a cara.
| Característica | Bash | PowerShell |
|---|---|---|
| Qué pasa por la tubería | Texto, línea a línea | Objetos .NET con propiedades |
| Sistemas | Linux y macOS; en Windows, con WSL o Git Bash | Windows; con PowerShell 7, también Linux y macOS |
| Nombres de comandos | Cortos e históricos: ls, grep, cp | Verbo-Sustantivo: Get-ChildItem, Copy-Item |
| Variables | nombre=valor, sin espacios; se lee con $nombre | $nombre = valor, siempre con $ |
| Parar ante errores | set -euo pipefail | $ErrorActionPreference = 'Stop' |
| Uso típico | Servidores Linux, contenedores, CI | Administración de Windows, Azure y Microsoft 365 |
Ponlo en práctica
Saber qué es cada cosa es la mitad del trabajo. La otra mitad es reconocer cuándo usarla.
Interactivo
¿Qué usarías?
Lee cada situación y elige la opción que mejor la resuelve. Fallar aquí no cuenta: es para practicar.
Quieres saber qué 10 mensajes de error se repiten más en el archivo de registro de un servidor Linux.
Para recordar
- En Bash, los programas se encadenan con
|y se pasan texto; en PowerShell, los cmdlets se pasan objetos con propiedades. >sobrescribe,>>añade,2>redirige los errores y2>&1los junta con la salida normal.- Rodea con comillas dobles las variables de Bash, o un espacio partirá tus rutas en dos.
- Empieza los scripts de Bash con un shebang y
set -euo pipefail: el código de salida 0 es éxito y cualquier otro, error. - PowerShell 7 (
pwsh) es multiplataforma; Windows PowerShell 5.1 sigue en Windows, pero ya no evoluciona.
Comprueba lo aprendido
Cuestionario
Responde las 5 preguntas para completar la lección.
1.¿Qué hace
./script.sh > salida.log 2>&1?2.La variable
archivovaleinforme final.pdf. ¿Qué comando borra ese archivo correctamente?3.¿Qué consigue
set -o pipefail?4.¿Cuál es la diferencia fundamental entre la tubería de Bash y la de PowerShell?
5.Un compañero propone poner
Set-ExecutionPolicy Unrestricteden todos los equipos para que «funcionen los scripts». ¿Qué le respondes?