Saltar al contenido principal

⚡ 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)

BloqueTemaDuración
1¿Qué es Bash? + Primer script20 min
2Variables y entrada del usuario25 min
3Condicionales (if/else)25 min
Break10 min
4Funciones y colores20 min
5Script de deploy automático35 min
6Cierre y próximos pasos15 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
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
Conceptos clave
  • #!/bin/bash (shebang): le dice al sistema que use Bash. Siempre va primero.
  • # son comentarios — Bash los ignora
  • echo imprime texto
  • $(comando) ejecuta un comando y usa su resultado (sustitución de comando)
Errores comunes
  • "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

variables.sh
#!/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:

  1. Al CREAR no lleva $: NOMBRE=valor
  2. Al USAR sí lleva $: $NOMBRE
  3. NO puede haber espacios alrededor del =
Error #1 de novatos

NOMBRE = "Carlos" da error. Siempre sin espacios: NOMBRE="Carlos"

read — Pedir datos al usuario

interactivo.sh
#!/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!"
Heredoc

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

condicional.sh
#!/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

info

Los espacios dentro de los corchetes son obligatorios. Y fi es if al revés — cierra el bloque.

Condiciones útiles

CondiciónPreguntaEjemplo
-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

colores.sh
#!/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

funciones.sh
#!/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"
info

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.

deploy.sh
#!/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

  1. Modificar el generador de perfil para agregar más campos (hobby, redes sociales)
  2. Crear verificar.sh que chequee: si es root, si existe la carpeta del sitio, si Nginx está corriendo
  3. Mejorar el script de deploy para que pregunte si querés Nginx o Apache y configure el que elijas

Apéndice: Troubleshooting de Bash

ErrorCausaSolución
Permission deniedFalta chmod +xchmod +x script.sh
command not foundFalta el ././script.sh
bad interpreterShebang mal escritoPrimera línea: #!/bin/bash
unexpected end of fileComilla sin cerrarRevisar comillas
NOMBRE: command not foundEspacios en variableNOMBRE="val" no NOMBRE = "val"
unary operator expectedVariable vacía en ifUsar comillas: [ "$VAR" == "x" ]
syntax error near fiFalta thenAgregar then después de condición
Heredoc no funcionaEOF con espaciosEOF de cierre al inicio de línea
Variables no se expandenEOF entre comillasUsar << EOF no << 'EOF'

Recursos