Saltar a contenido

📚 Módulo 3: Tomando decisiones con tu terminal

Duración estimada: 3-4 horas según tu ritmo y experiencia.

Objetivo: Usar condicionales (if, case) para que tus scripts tomen decisiones, y aprender bucles for para automatizar tareas repetitivas. También aprenderás a depurar scripts cuando algo falla. 🎯

Prerrequisitos: Haber completado los Módulos 1 y 2, saber crear y ejecutar scripts básicos con variables y redirecciones.

Nota: Los condicionales y bucles son la base de cualquier script no trivial. Practica cada ejemplo antes de continuar.


📖 Contenidos del Módulo

3.1. Condicionales básicos: if y else 🌱

Teoría:

Los condicionales permiten ejecutar diferentes bloques de código según una condición. La estructura básica en Bash es:

if [ condición ]; then
    # código si la condición es verdadera
else
    # código si la condición es falsa
fi

Los corchetes [ ] evalúan la condición. Los operadores más habituales para números son -eq (igual), -ne (distinto), -gt (mayor que), -lt (menor que), -ge (mayor o igual) y -le (menor o igual). Para comparar texto se usa = y !=.

Buena práctica: Escribe siempre las variables entre comillas dentro de los corchetes: [ "$variable" -gt 5 ]. Así evitas errores si la variable está vacía o contiene espacios.

Práctica:

  1. Crea un script decision.sh:
nano decision.sh
  1. Escribe un condicional básico:
#!/bin/bash
numero=10
if [ "$numero" -gt 5 ]; then
    echo "El número es mayor que 5"
else
    echo "El número es menor o igual a 5"
fi

Guarda, dale permisos (chmod +x decision.sh) y ejecútalo.

  1. Cambia el valor de numero a 3 y vuelve a ejecutar para ver el otro caso.

  2. Añade una condición intermedia con elif:

nano decision.sh
#!/bin/bash
numero=5
if [ "$numero" -gt 10 ]; then
    echo "Mayor que 10"
elif [ "$numero" -eq 5 ]; then
    echo "Exactamente 5"
else
    echo "Menor que 5"
fi
  1. Prueba con distintos valores de numero (0, 5, 15) para verificar los tres casos.

Consejo: elif es la abreviatura de "else if". Puedes encadenar tantos elif como necesites. 💡


3.2. Condicionales avanzados: case 🖌️

Teoría:

case es una alternativa más limpia a encadenar múltiples elif cuando quieres comparar una variable contra varios valores concretos. Su sintaxis es:

case "$variable" in
    patron1) comandos ;;
    patron2) comandos ;;
    *) comandos por defecto ;;
esac

El * actúa como comodín para cualquier valor no contemplado en los patrones anteriores. Es especialmente útil en menús interactivos.

Práctica:

  1. Crea un script menu.sh:
nano menu.sh
  1. Escribe un menú básico con case:
#!/bin/bash
opcion="salir"
case "$opcion" in
    iniciar)  echo "Iniciando el sistema..." ;;
    detener)  echo "Deteniendo el sistema..." ;;
    salir)    echo "Saliendo..." ;;
    *)        echo "Opción no reconocida: $opcion" ;;
esac

Guarda, dale permisos y ejecútalo. Luego cambia opcion a iniciar y detener para probar.

  1. Añade entrada del usuario con read:
nano menu.sh
#!/bin/bash
read -p "Elige una opción (iniciar/detener/salir): " opcion
case "$opcion" in
    iniciar)  echo "Iniciando el sistema..." ;;
    detener)  echo "Deteniendo el sistema..." ;;
    salir)    echo "¡Hasta luego!" ;;
    *)        echo "Opción '$opcion' no reconocida." ;;
esac
  1. Ejecuta el script varias veces introduciendo diferentes opciones, incluyendo una no válida.

  2. Añade un patrón que acepte tanto mayúsculas como minúsculas para salir:

salir|SALIR) echo "¡Hasta luego!" ;;

El | dentro de case permite múltiples patrones para la misma acción.

Dato curioso: case es mucho más legible que una cadena larga de elif cuando tienes más de tres opciones. 😄


3.3. Comparando texto y archivos ⚙️

Teoría:

Bash permite evaluar condiciones sobre cadenas de texto y sobre el sistema de archivos. Algunos operadores útiles:

  • [ "$a" = "$b" ] — las cadenas son iguales
  • [ "$a" != "$b" ] — las cadenas son distintas
  • [ -z "$a" ] — la cadena está vacía
  • [ -n "$a" ] — la cadena no está vacía
  • [ -f archivo ] — el archivo existe y es un fichero regular
  • [ -d directorio ] — el directorio existe
  • [ -e ruta ] — la ruta existe (archivo o directorio)

Práctica:

  1. Comprueba si una cadena está vacía:
nano texto.sh
#!/bin/bash
cadena=""
if [ -z "$cadena" ]; then
    echo "La cadena está vacía"
else
    echo "La cadena contiene: $cadena"
fi

Guarda, dale permisos y prueba cambiando el valor de cadena.

  1. Verifica si un archivo existe:
nano archivo.sh
#!/bin/bash
if [ -f "salida.txt" ]; then
    echo "El archivo salida.txt existe"
else
    echo "El archivo salida.txt NO existe"
fi
  1. Ejecuta el script, luego crea el archivo y vuelve a ejecutarlo:
chmod +x archivo.sh
./archivo.sh
touch salida.txt
./archivo.sh
  1. Añade una verificación de directorio:
if [ -d "mi_aventura" ]; then
    echo "El directorio mi_aventura existe"
fi
  1. Combina condiciones con && (y) y || (o):
nano combo.sh
#!/bin/bash
archivo="salida.txt"
if [ -f "$archivo" ] && [ -n "$archivo" ]; then
    echo "El archivo existe y tiene nombre"
fi

Consejo: Comprueba siempre la existencia de archivos antes de trabajar con ellos para evitar errores inesperados en tus scripts. 💡


3.4. Bucles for: Repite con elegancia 📜

Teoría:

El bucle for repite un bloque de comandos para cada elemento de una lista. En Bash hay varias formas de usarlo. De menor a mayor complejidad:

# Lista literal
for elemento in uno dos tres; do
    echo "$elemento"
done

# Rango con llaves
for i in {1..5}; do
    echo "Iteración $i"
done

# Secuencia con seq (útil para rangos dinámicos)
for i in $(seq 1 5); do
    echo "Número $i"
done

# Globbing: todos los archivos .sh del directorio
for archivo in *.sh; do
    echo "Script: $archivo"
done

Práctica:

  1. Crea un script bucle.sh con una lista literal:
nano bucle.sh
#!/bin/bash
for fruta in manzana naranja pera; do
    echo "Fruta: $fruta"
done

Guarda, dale permisos y ejecútalo.

  1. Usa un rango con llaves para contar:
nano bucle.sh
#!/bin/bash
for i in {1..5}; do
    echo "Número: $i"
done
  1. Usa seq para un rango con paso personalizado:
nano bucle.sh
#!/bin/bash
# Contar de 2 en 2 hasta 10
for i in $(seq 2 2 10); do
    echo "Par: $i"
done

La sintaxis de seq es: seq inicio paso fin.

  1. Recorre todos los scripts del directorio con globbing:
nano bucle.sh
#!/bin/bash
for archivo in *.sh; do
    echo "Encontrado: $archivo"
done
  1. Crea varios archivos en un bucle y luego lístalos:
nano crear.sh
#!/bin/bash
for i in {1..3}; do
    touch "archivo_$i.txt"
    echo "Creado archivo_$i.txt"
done
ls *.txt

Consejo: Usa {inicio..fin} para rangos simples y $(seq inicio paso fin) cuando el inicio o el fin sean variables. 💡


3.5. Depuración: Encuentra y corrige errores 🚀

Teoría:

Bash incluye opciones de depuración muy útiles que se activan con el flag -x (modo trace). Puedes usarlo al ejecutar el script o añadirlo en el propio código:

  • bash -x script.sh — muestra cada comando antes de ejecutarlo.
  • set -x dentro del script — activa el trace desde ese punto.
  • set +x — desactiva el trace.
  • set -e — hace que el script se detenga inmediatamente si cualquier comando falla.

Práctica:

  1. Crea un script con un error intencional:
nano error.sh
#!/bin/bash
echo "Inicio del script"
ls /ruta/inexistente
echo "Fin del script"

Ejecútalo y observa qué pasa: el error se muestra pero el script continúa.

  1. Activa el modo trace para ver qué ejecuta cada línea:
bash -x error.sh

Cada línea precedida por + muestra el comando que se va a ejecutar.

  1. Añade set -e para detener el script en el primer error:
nano error.sh
#!/bin/bash
set -e
echo "Inicio del script"
ls /ruta/inexistente
echo "Esta línea NO se ejecutará"
  1. Combina set -e y set -x para depuración completa:
nano debug.sh
#!/bin/bash
set -ex
nombre="Debug"
echo "Hola, $nombre"
ls /tmp
  1. Usa set -x y set +x para depurar solo una sección:
nano parcial.sh
#!/bin/bash
echo "Sección normal"
set -x
ls /tmp
set +x
echo "Vuelvo al modo normal"

Advertencia: Quita set -x antes de pasar el script a producción; el trace genera mucho ruido en los logs. 🛑


🛠️ Ejercicios prácticos

  1. Condicional: Crea un script que compruebe si un archivo datos.txt existe y muestre un mensaje apropiado en cada caso.
  2. Case: Escribe un menú de 4 opciones (crear, listar, borrar, salir) que muestre un mensaje para cada una.
  3. Bucle: Crea un script que genere los archivos nota_1.txt hasta nota_5.txt con el texto "Nota número N".
  4. Depuración: Introduce un error en uno de tus scripts y usa bash -x para localizarlo.
  5. Desafío: Crea un script que liste los archivos .txt del directorio y muestre si cada uno está vacío o no (pista: [ -s archivo ] comprueba si el archivo tiene contenido).

📝 Evaluación

Cuestionario (5 preguntas):

  1. ¿Cuándo usarías case en lugar de if/elif?
  2. ¿Qué hace [ -f archivo ] en una condición?
  3. ¿Cómo recorres todos los archivos .txt de un directorio con for?
  4. ¿Para qué sirve bash -x script.sh?
  5. ¿Qué diferencia hay entre {1..5} y $(seq 1 5)?

Tarea práctica:

Crea un script organiza.sh que: - Compruebe si existe una carpeta backup; si no, la cree. - Recorra todos los archivos .txt del directorio actual y los copie a backup/. - Muestre un mensaje al finalizar indicando cuántos archivos copió.

Entrega: El script y una breve explicación de cómo funciona cada sección.

🎬 Vídeo con la solución de la evaluación: Próximamente.


🎉 Recursos adicionales

  • Documentación: man bash (sección "Compound Commands").
  • Referencia de condicionales: Bash Conditional Expressions (GNU).
  • Práctica interactiva: ShellCheck — analiza tus scripts y señala errores comunes en tiempo real.

Tip: ShellCheck es una herramienta gratuita que detecta errores frecuentes en scripts Bash antes de ejecutarlos. Úsala siempre que tengas dudas. 💡


🚀 Siguientes pasos

¡Felicidades por completar el Módulo 3! En el Módulo 4 aprenderás a procesar texto con herramientas como grep, sed y awk, y a trabajar con expresiones regulares. ¡El procesamiento de datos te espera! ✍️