Scripts de Bash a prueba de balas: los trucos que me salvan en producción

Scripts de Bash a prueba de balas: los trucos que me salvan en producción

Todos los equipos tienen ese script que nació como cinco líneas para salir del paso y hoy sostiene medio proceso de despliegue. Bash sigue siendo el pegamento de la infraestructura: une pipelines, contenedores, crons, backups y migraciones. Y precisamente porque está en todas partes, un script frágil puede hacer muchísimo daño. Después de años escribiendo (y depurando) scripts en producción, estos son los trucos que más sustos me han ahorrado.

Arranca siempre en modo estricto

La primera línea que escribo después del shebang es siempre la misma. El modo estricto hace que el script falle pronto y de forma ruidosa, en lugar de seguir ejecutando comandos sobre un estado roto, que es exactamente como se fabrican los desastres silenciosos.

#!/usr/bin/env bash
set -euo pipefail

Con set -e el script se detiene ante el primer error. Con set -u, usar una variable sin definir deja de ser un silencio peligroso y se convierte en un fallo visible al instante. Y pipefail evita que una tubería enmascare el error de un comando intermedio: sin él, curl puede fallar y el script seguirá tan contento porque el último comando de la tubería terminó bien. Tres opciones, una línea, y la mitad de los sustos fuera.

Comillas siempre y valores por defecto

La mayoría de los fallos raros en Bash vienen de expansiones sin comillas: una ruta con espacios, una variable vacía, un asterisco que se expande cuando no toca. Mi regla es simple y no negociable: toda variable va entre comillas dobles. Y para los parámetros opcionales, las expansiones con valor por defecto ahorran páginas de condicionales.

ENTORNO="${1:-staging}"
RUTA_LOGS="${RUTA_LOGS:-/var/log/miapp}"
echo "Desplegando en ${ENTORNO}"

Ese ${1:-staging} significa: usa el primer argumento y, si no existe, usa staging. Combinado con set -u, el script documenta sus propios valores por defecto y falla con claridad cuando falta algo obligatorio. Autodocumentación gratis.

trap: limpia aunque todo salga mal

Un script que crea ficheros temporales, adquiere un lock o levanta un túnel tiene la obligación de dejarlo todo limpio también cuando falla. Para eso existe trap, probablemente la herramienta más infrautilizada del lenguaje.

TMP_DIR="$(mktemp -d)"
cleanup() {
  rm -rf "${TMP_DIR}"
}
trap cleanup EXIT

La función cleanup se ejecutará siempre al salir: con éxito, con error o si alguien mata el proceso a media ejecución. Es la diferencia entre un servidor sembrado de directorios temporales huérfanos y un script del que te puedes fiar aunque las cosas se tuerzan.

Hábitos pequeños, diferencia enorme

  • Pasa shellcheck en el CI: este linter detecta la inmensa mayoría de los errores clásicos de Bash antes de que lleguen a producción. Si solo adoptas un hábito de esta lista, que sea este: el retorno de inversión es inmediato.
  • Estructura con funciones y un main: aunque sea Bash, el orden importa. Funciones cortas con nombres claros y un main al final que las orquesta hacen el script legible, y lo legible se mantiene solo.
  • Logs con contexto: un prefijo con fecha y nombre del script convierte un cron misterioso que falla de madrugada en algo que se puede depurar sin hacer arqueología.
  • Modo depuración bajo demanda: respeta una variable DEBUG que active set -x para imprimir cada comando ejecutado. Cuando algo solo falla en el servidor y no en tu máquina, es oro puro.

Nada de esto es sofisticado, y esa es justo la gracia: son hábitos baratos que se amortizan con el primer incidente evitado. Bash no va a desaparecer de nuestras vidas, así que lo más sensato es tratar los scripts como lo que realmente son: código de producción, con los mismos derechos y deberes que el resto. ¿Cuál es el truco de Bash que a ti te ha salvado de un buen susto? Te leo en los comentarios 😊