⚡ Bash Scripting: Automatizá tu Deploy
Continuación de la Clase 1. Ya sabés hacer deploy a mano. Ahora lo automatizamos con scripts de Bash.
Objetivos
- Entender qué es Bash y para qué sirve un script
- Crear y ejecutar scripts
- Usar variables y entrada del usuario (
read) - Tomar decisiones con condicionales (
if/else) - Organizar código con funciones
- Construir un script de deploy automático completo
Prerequisitos
Haber completado la Clase 01 o saber: moverse en terminal (cd, ls, pwd), crear archivos (nano, cat, mkdir) y tener el concepto de servidor web.
👉 Practicá en el Playground ROXS en KillerCoda
Estructura de tiempos (~2h 30min)
| Bloque | Tema | Duración |
|---|---|---|
| 1 | ¿Qué es Bash? + Primer script | 20 min |
| 2 | Variables y entrada del usuario | 25 min |
| 3 | Condicionales (if/else) | 25 min |
| ☕ | Break | 10 min |
| 4 | Funciones y colores | 20 min |
| 5 | Script de deploy automático | 35 min |
| 6 | Cierre y próximos pasos | 15 min |
Bloque 1 — ¿Qué es Bash? + Mi primer script
Bash es el programa que lee e interpreta los comandos en la terminal. Un script es un archivo con una lista de comandos que se ejecutan en orden.
SIN SCRIPT (clase pasada): CON SCRIPT (hoy):
$ apt update $ ./deploy.sh
$ apt install nginx -y (se ejecutan TODOS
$ mkdir -p /var/www/mi-sitio los comandos solos)
...
8 comandos a mano → 1 solo comando
Mi primer script
mkdir ~/scripts && cd ~/scripts
nano hola.sh
#!/bin/bash
# Mi primer script de Bash
echo "================================"
echo " ¡Hola! Soy un script de Bash"
echo "================================"
echo ""
echo "Fecha actual:"
date
echo ""
echo "Usuario: $(whoami)"
echo "Directorio: $(pwd)"
echo ""
echo "✅ ¡Script ejecutado con éxito!"
chmod +x hola.sh # Dar permiso de ejecución
./hola.sh # Ejecutar
#!/bin/bash(shebang): le dice al sistema que use Bash. Siempre va primero.#son comentarios — Bash los ignoraechoimprime texto$(comando)ejecuta un comando y usa su resultado (sustitución de comando)
- "Permission denied" → Falta
chmod +x - "command not found" → Falta el
./antes del nombre - "bad interpreter" → Error en la primera línea, verificar
#!/bin/bash
Bloque 2 — Variables y entrada del usuario
Variables
#!/bin/bash
# CREAR: NOMBRE=valor (SIN espacios en el =)
# USAR: $NOMBRE (con signo $)
NOMBRE="Carlos"
SITIO="mi-pagina-web"
RUTA="/var/www/$SITIO"
echo "Hola, me llamo $NOMBRE"
echo "Mi sitio: $SITIO"
echo "Ruta completa: $RUTA"
echo ""
echo "Variables del sistema:"
echo " Usuario: $USER"
echo " Home: $HOME"
echo " Shell: $SHELL"
Las 3 reglas:
- Al CREAR no lleva
$:NOMBRE=valor - Al USAR sí lleva
$:$NOMBRE - NO puede haber espacios alrededor del
=
NOMBRE = "Carlos" da error. Siempre sin espacios: NOMBRE="Carlos"
read — Pedir datos al usuario
#!/bin/bash
echo "╔═══════════════════════════════╗"
echo "║ 🚀 Generador de Perfil Web ║"
echo "╚═══════════════════════════════╝"
echo ""
read -p "¿Cómo te llamás? → " NOMBRE
read -p "¿Tu profesión? → " PROFESION
read -p "¿Color favorito (en inglés)? → " COLOR
echo "Generando tu perfil..."
cat > /root/perfil.html << EOF
<!DOCTYPE html>
<html>
<head><title>$NOMBRE</title>
<style>
body{font-family:Arial;background:$COLOR;color:white;
display:flex;align-items:center;justify-content:center;
min-height:100vh;text-align:center}
h1{font-size:3rem}p{font-size:1.3rem;opacity:.8}
</style></head>
<body><div>
<h1>👋 $NOMBRE</h1>
<p>$PROFESION</p>
<p>Generado con Bash</p>
</div></body></html>
EOF
echo "✅ ¡Perfil creado en /root/perfil.html!"
El truco cat > archivo << EOF ... EOF se llama "heredoc". Escribe muchas líneas en un archivo y las variables se reemplazan automáticamente. Es como un template.
Bloque 3 — Condicionales: if / else
#!/bin/bash
# Verificar si sos root
if [ "$USER" == "root" ]; then
echo "✅ Sos root, tenés permisos de admin"
else
echo "❌ No sos root. Usá: sudo ./script.sh"
exit 1
fi
# Verificar si Nginx está instalado
if command -v nginx &> /dev/null; then
echo "✅ Nginx ya está instalado"
else
echo "⚠️ Nginx NO está instalado"
read -p "¿Instalarlo? (s/n): " RESP
if [ "$RESP" == "s" ]; then
apt update -qq && apt install nginx -y -qq
echo "✅ ¡Nginx instalado!"
fi
fi
# Verificar si existe un archivo
if [ -f "/var/www/html/index.html" ]; then
echo "✅ El archivo index.html existe"
else
echo "❌ El archivo NO existe"
fi
Sintaxis: if [ condición ]; then ... else ... fi
Los espacios dentro de los corchetes son obligatorios. Y fi es if al revés — cierra el bloque.
Condiciones útiles
| Condición | Pregunta | Ejemplo |
|---|---|---|
-f archivo | ¿Existe el archivo? | [ -f "/var/www/index.html" ] |
-d carpeta | ¿Existe la carpeta? | [ -d "/var/www/mi-sitio" ] |
"$A" == "$B" | ¿Son iguales? | [ "$USER" == "root" ] |
-z "$VAR" | ¿Está vacía? | [ -z "$NOMBRE" ] |
command -v X | ¿Está instalado? | command -v nginx |
Bloque 4 — Funciones y colores
Colores en la terminal
#!/bin/bash
VERDE='\033[0;32m'
ROJO='\033[0;31m'
AMARILLO='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m' # Reset — siempre cerrar con esto
echo -e "${VERDE}✅ Mensaje de ÉXITO${NC}"
echo -e "${ROJO}❌ Mensaje de ERROR${NC}"
echo -e "${AMARILLO}⚠️ ADVERTENCIA${NC}"
echo -e "${CYAN}ℹ️ INFORMACIÓN${NC}"
Funciones
#!/bin/bash
VERDE='\033[0;32m'
ROJO='\033[0;31m'
CYAN='\033[0;36m'
NC='\033[0m'
# Definir funciones
exito() { echo -e "${VERDE}✅ $1${NC}"; }
error() { echo -e "${ROJO}❌ $1${NC}"; }
info() { echo -e "${CYAN}ℹ️ $1${NC}"; }
# Función más compleja
instalar() {
if command -v $1 &> /dev/null; then
info "$1 ya está instalado"
else
info "Instalando $1..."
apt install $1 -y -qq
exito "$1 instalado"
fi
}
# Usar las funciones
exito "Todo bien"
error "Algo salió mal"
instalar "curl"
instalar "git"
Se define con nombre() { ... }. Se llama solo con el nombre. $1 es el primer argumento que le pasamos.
Bloque 5 — Script de deploy automático completo
Este es el proyecto final: todo lo aprendido en un solo script profesional.
#!/bin/bash
# ═══════════════════════════════════════
# 🚀 Script de Deploy Automático
# ═══════════════════════════════════════
# ── Colores ──
VERDE='\033[0;32m'
ROJO='\033[0;31m'
AMARILLO='\033[1;33m'
CYAN='\033[0;36m'
NEGRITA='\033[1m'
NC='\033[0m'
# ── Funciones ──
exito() { echo -e "${VERDE} ✅ $1${NC}"; }
error() { echo -e "${ROJO} ❌ $1${NC}"; }
info() { echo -e "${CYAN} ℹ️ $1${NC}"; }
warn() { echo -e "${AMARILLO} ⚠️ $1${NC}"; }
titulo() { echo -e "\n${NEGRITA}${CYAN}$1${NC}"; }
# ── Banner ──
clear
echo -e "${NEGRITA}${CYAN}"
echo "╔═══════════════════════════════════════╗"
echo "║ 🚀 DEPLOY AUTOMÁTICO DE SITIO WEB ║"
echo "╚═══════════════════════════════════════╝"
echo -e "${NC}\n"
# ── Verificar root ──
if [ "$EUID" -ne 0 ]; then
error "Necesitás permisos root. Usá: sudo ./deploy.sh"
exit 1
fi
# ── Pedir datos ──
read -p " Nombre del sitio (ej: mi-portfolio): " SITIO
read -p " Tu nombre: " AUTOR
read -p " Descripción corta: " DESC
if [ -z "$SITIO" ]; then
error "El nombre no puede estar vacío"; exit 1
fi
SITIO_DIR="/var/www/$SITIO"
NGINX_CONF="/etc/nginx/sites-available/$SITIO"
# ═══ PASO 1: INSTALAR NGINX ═══
titulo "📦 Paso 1: Verificar e instalar Nginx"
if command -v nginx &> /dev/null; then
exito "Nginx ya está instalado"
else
info "Instalando Nginx..."
apt update -qq && apt install nginx -y -qq
exito "Nginx instalado"
fi
# ═══ PASO 2: CREAR SITIO WEB ═══
titulo "🎨 Paso 2: Crear sitio web"
mkdir -p "$SITIO_DIR"
cat > "$SITIO_DIR/index.html" << EOF
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>$SITIO</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'Segoe UI',sans-serif;
background:linear-gradient(135deg,#0f0c29,#302b63,#24243e);
color:white;min-height:100vh;display:flex;
align-items:center;justify-content:center}
.c{text-align:center;padding:2rem}
h1{font-size:3rem;margin-bottom:.5rem}
.d{font-size:1.3rem;opacity:.7;margin-bottom:2rem}
.b{display:inline-block;background:rgba(255,255,255,.1);
border:1px solid rgba(255,255,255,.2);padding:.5rem 1.5rem;
border-radius:50px;font-size:.9rem}
.f{margin-top:3rem;font-size:.8rem;opacity:.4}
</style>
</head>
<body><div class="c">
<h1>🚀 $SITIO</h1>
<p class="d">$DESC</p>
<div class="b">Creado por $AUTOR</div>
<p class="f">Deploy automático con Bash + Nginx</p>
</div></body></html>
EOF
exito "HTML generado"
# ═══ PASO 3: CONFIGURAR NGINX ═══
titulo "⚙️ Paso 3: Configurar Nginx"
cat > "$NGINX_CONF" << NGINXEOF
server {
listen 80;
listen [::]:80;
server_name _;
root $SITIO_DIR;
index index.html;
location / { try_files \$uri \$uri/ =404; }
}
NGINXEOF
if [ -f /etc/nginx/sites-enabled/default ]; then
rm /etc/nginx/sites-enabled/default
fi
ln -sf "$NGINX_CONF" /etc/nginx/sites-enabled/
chown -R www-data:www-data "$SITIO_DIR"
chmod -R 755 "$SITIO_DIR"
exito "Nginx configurado y permisos aplicados"
# ═══ PASO 4: VERIFICAR Y REINICIAR ═══
titulo "🔍 Paso 4: Verificar y reiniciar"
if nginx -t 2>/dev/null; then
exito "Configuración válida"
else
error "Error en la configuración. Ejecutá 'nginx -t'"; exit 1
fi
systemctl restart nginx
if systemctl is-active --quiet nginx; then
exito "Nginx corriendo"
else
error "Nginx no arrancó"; exit 1
fi
# ═══ RESUMEN ═══
echo ""
echo -e "${NEGRITA}${CYAN}════════════════════════════════════${NC}"
echo -e "${NEGRITA}${VERDE} 🎉 ¡DEPLOY COMPLETADO!${NC}"
echo -e "${NEGRITA}${CYAN}════════════════════════════════════${NC}"
info "Sitio: $SITIO"
info "Autor: $AUTOR"
info "Carpeta: $SITIO_DIR"
info "URL: http://localhost"
echo ""
exito "¡Tu sitio está LIVE! 🚀"
chmod +x deploy.sh && ./deploy.sh
Bloque 6 — Recapitulación
✅ Qué es Bash y qué es un script (shebang, chmod +x, ./)
✅ Variables: NOMBRE=valor y $NOMBRE
✅ Input del usuario: read -p
✅ Condicionales: if / else / fi
✅ Funciones reutilizables
✅ Colores en la terminal
✅ Heredocs (cat > archivo << EOF)
✅ ¡Un script de deploy automático completo!
Ejercicios
- Modificar el generador de perfil para agregar más campos (hobby, redes sociales)
- Crear
verificar.shque chequee: si es root, si existe la carpeta del sitio, si Nginx está corriendo - Mejorar el script de deploy para que pregunte si querés Nginx o Apache y configure el que elijas
Apéndice: Troubleshooting de Bash
| Error | Causa | Solución |
|---|---|---|
Permission denied | Falta chmod +x | chmod +x script.sh |
command not found | Falta el ./ | ./script.sh |
bad interpreter | Shebang mal escrito | Primera línea: #!/bin/bash |
unexpected end of file | Comilla sin cerrar | Revisar comillas |
NOMBRE: command not found | Espacios en variable | NOMBRE="val" no NOMBRE = "val" |
unary operator expected | Variable vacía en if | Usar comillas: [ "$VAR" == "x" ] |
syntax error near fi | Falta then | Agregar then después de condición |
| Heredoc no funciona | EOF con espacios | EOF de cierre al inicio de línea |
| Variables no se expanden | EOF entre comillas | Usar << EOF no << 'EOF' |
Recursos
- 🏋️ Ejercicios de esta clase
- 📋 Ejemplos de esta clase
- Bash Reference Manual
- ShellCheck — Linter online para scripts de Bash
- ExplainShell — Explica comandos de shell
- Playground ROXS — Escenarios del bootcamp
- 90 Días de DevOps — El challenge completo
- roxs.dev — Web de ROXS
- ▶️ Playlist del Bootcamp
- 🎮 Twitch — roxsross