Curso de Shell Scripting
Cuando un script crece y se usa en producción, no basta con que funcione: necesita ser predecible, fácil de depurar y resistente a fallos.
En este módulo veremos:
- El shebang correcto.
- Opciones de Bash para scripts seguros.
- Manejo de errores y limpieza con
trap. - Estructura mínima de un proyecto de scripts.
3.1. Shebang recomendado
Al inicio de todo script ejecutable pon:
1#!/usr/bin/env bash
✅ Busca la versión de Bash disponible en el entorno.
✅ Es más portátil que #!/bin/bash.
⚠️ Sin shebang, el script podría ejecutarse con el shell por defecto del sistema (por ejemplo
sh), y fallar si usas características específicas de Bash.
3.2. Opciones de Bash para seguridad
Coloca estas opciones justo después del shebang:
1set -Eeuo pipefail
Vamos a entenderlas:
| Opción | Qué hace |
|---|---|
-E | Hace que los traps de ERR también se activen en funciones o subshells. |
-e | Hace que el script se detenga si un comando falla (retorna ≠ 0). |
-u | Provoca error si usas una variable no definida (evita errores sutiles). |
-o pipefail | Si en una tubería un comando falla, el fallo se propaga al script. |
⚡ Ejemplo sin opciones
1#!/usr/bin/env bash 2 3echo "Inicio" 4comando_inexistente 5echo "Fin"
Salida:
Inicio
./script.sh: línea 3: comando_inexistente: no se encontró el comando
Fin
👉 El script siguió ejecutando a pesar del error.
⚡ Ejemplo con set -Eeuo pipefail
1#!/usr/bin/env bash 2set -Eeuo pipefail 3 4echo "Inicio" 5comando_inexistente 6echo "Fin"
Salida:
Inicio
./script.sh: línea 5: comando_inexistente: no se encontró el comando
👉 El script se detiene al encontrar el error. Esto evita que se sigan ejecutando pasos que dependen de uno que falló.
3.3. Limpieza y manejo de errores con trap
trap permite ejecutar acciones cuando ocurren eventos específicos.
Sintaxis:
1trap 'comando_a_ejecutar' EVENTO
Eventos más usados:
| Evento | Cuándo ocurre |
|---|---|
EXIT | Al finalizar el script (éxito o fallo). |
ERR | Cuando falla un comando (con set -e). |
INT | Cuando el usuario interrumpe con Ctrl+C. |
TERM | Cuando el sistema pide terminar el proceso. |
⚡ Ejemplo práctico: limpieza de temporales
1#!/usr/bin/env bash 2set -Eeuo pipefail 3 4TMPFILE=$(mktemp) # crea un archivo temporal 5echo "Archivo temporal creado: $TMPFILE" 6 7# Limpieza automática al salir 8trap 'rm -f "$TMPFILE"' EXIT 9 10echo "Escribiendo datos..." 11echo "Hola mundo" > "$TMPFILE" 12 13# Simulamos un fallo 14false 15 16echo "Esto nunca se ejecuta"
Salida:
Archivo temporal creado: /tmp/tmp.abcd1234
Escribiendo datos...
👉 Aunque el script falla, el archivo temporal se elimina automáticamente gracias al trap.
3.4. Estructura mínima de proyecto
A medida que los scripts crecen, es mejor organizarlos. Ejemplo de estructura:
mi_proyecto/
├── bin/ # Scripts ejecutables
│ └── main.sh
├── lib/ # Funciones reutilizables
│ └── utils.sh
├── tests/ # Pruebas de los scripts
└── README.md
⚡ Ejemplo de main.sh
1#!/usr/bin/env bash 2set -Eeuo pipefail 3trap 'echo "Ocurrió un error o se cerró el script"' EXIT 4 5source "$(dirname "$0")/../lib/utils.sh" 6 7echo "Inicio del script principal" 8saludar "Angel"
⚡ Ejemplo de utils.sh
1#!/usr/bin/env bash 2 3saludar() { 4 local nombre="$1" 5 echo "¡Hola, $nombre! Bienvenido al script." 6}
Ejecución:
1chmod +x bin/main.sh 2./bin/main.sh
Salida:
Inicio del script principal
¡Hola, Angel! Bienvenido al script.
Ocurrió un error o se cerró el script
👉 El mensaje final se ejecuta al salir por el trap.
🏋️♂️ Ejercicio práctico
Crea un proyecto llamado backup_logs con esta estructura:
backup_logs/
├── bin/
│ └── backup.sh
└── lib/
└── funciones.sh
El script backup.sh debe:
- Activar
set -Eeuo pipefail. - Usar un
trappara mostrar “Backup finalizado” al terminar (éxito o error). - Crear un directorio
backups/(si no existe). - Copiar todos los
.logde la carpeta actual abackups/usandocp. - Mostrar cuántos archivos se copiaron.
- En caso de fallo, detenerse.
💡 Pistas:
- Usa
mkdir -p backups. - Usa
cp *.log backups/pero controla el error si no hay.log. - Usa
ls backups/*.log | wc -lpara contar.
- Loading...