Dominando la Depuración de Scripts Bash: Técnicas Esenciales para Desarrolladores
Depura scripts Bash con verificaciones de sintaxis, xtrace, modo estricto, trampas, ShellCheck y registro enfocado.
Dominando la Depuración de Scripts Bash: Técnicas Esenciales para Desarrolladores
La depuración de scripts Bash comienza con una pregunta: ¿dónde hizo el script algo diferente de lo que esperabas? Una buena depuración te brinda visibilidad sobre errores de sintaxis, variables expandidas, órdenes de comandos y estados de salida sin convertir el script en ruido.
Esta guía recorre técnicas prácticas de depuración de Bash que puedes usar en un script de automatización real antes de que llegue a cron, CI o producción.
Comienza con una Verificación de Sintaxis
Antes de rastrear el comportamiento en tiempo de ejecución, asegúrate de que Bash pueda analizar el archivo:
bash -n ./deploy.sh
bash -n lee el script e informa errores de sintaxis sin ejecutar comandos. Detecta fi, done, then, comillas y llaves faltantes. No detectará errores de lógica, archivos faltantes o comandos que fallen en tiempo de ejecución.
Por ejemplo, este error tipográfico se detecta antes de que se ejecute algo:
if [ -f "$CONFIG" ]; then
echo "Config encontrada"
# falta fi
Ejecuta una verificación de sintaxis después de grandes ediciones y antes de agregar más salida de depuración.
Rastrea la Ejecución con set -x
El depurador integrado más útil es xtrace:
set -x
algún_comando "$VALOR"
set +x
Con el rastreo habilitado, Bash imprime cada comando después de las expansiones y antes de la ejecución. Eso te ayuda a ver si una variable está vacía, si un glob se expandió o si un comando recibió argumentos diferentes a los esperados.
Para rastrear todo el script, ejecuta:
bash -x ./deploy.sh
Para rastros más limpios, establece PS4 para que cada línea incluya el número de línea de origen:
export PS4='+ ${BASH_SOURCE}:${LINENO}: '
bash -x ./deploy.sh
Si tu script maneja secretos, no rastrees secciones que impriman tokens, contraseñas o URLs firmadas. Desactiva el rastreo antes de esos comandos:
set +x
login_con_secreto "$API_TOKEN"
set -x
Agrega el Modo Estricto con Cuidado
Estas opciones detectan fallos comunes más temprano:
set -euo pipefail
set -e sale en muchos fallos de comandos no manejados. set -u trata las variables no establecidas como errores. set -o pipefail hace que una tubería falle si algún comando en la tubería falla, no solo el último comando.
Son útiles, pero no sustituyen el manejo explícito. Comandos como grep pueden devolver 1 para un resultado normal de "no encontrado":
if grep -q "LISTO" estado.txt; then
echo "listo"
else
echo "no listo"
fi
Eso es más claro que ocultar el resultado con grep -q "LISTO" estado.txt || true.
Imprime los Valores Correctos
Un registro enfocado supera a las líneas echo dispersas. Imprime los valores que afectan la rama que estás depurando:
printf 'DEBUG: usuario=%q entorno=%q destino=%q\n' "$NOMBRE_USUARIO" "$ENTORNO" "$HOST_DESTINO" >&2
printf '%q' muestra valores escapados para el shell, lo que facilita detectar espacios y caracteres especiales. Envía la salida de depuración a stderr para que la salida normal del script siga siendo utilizable en tuberías.
Cuando un comando falla, captura su estado inmediatamente:
ejecutar_migracion
estado=$?
if [ "$estado" -ne 0 ]; then
echo "La migración falló con código de salida $estado" >&2
exit "$estado"
fi
No ejecutes otro comando antes de guardar $?, porque incluso echo lo reemplaza.
Depura Bucles y Condicionales
Los errores de bucle a menudo provienen de división de palabras o entrada inesperada. Cita variables y lee líneas de manera segura:
while IFS= read -r linea; do
printf 'linea=%q\n' "$linea" >&2
done < entrada.txt
Para condicionales, imprime los valores exactos que se están comparando:
printf 'esperado=%q actual=%q\n' "$ESPERADO" "$ACTUAL" >&2
if [[ "$ACTUAL" == "$ESPERADO" ]]; then
echo "coincidencia"
fi
Si necesitas pausar dentro de un script durante la depuración local, read funciona:
read -r -p "Presiona Enter para continuar..."
Elimina las pausas antes de confirmar el script, especialmente si puede ejecutarse sin supervisión.
Usa ShellCheck para Análisis Estático
ShellCheck detecta muchos problemas que Bash ejecutará felizmente hasta que fallen en un caso extremo:
shellcheck ./deploy.sh
Señala variables sin comillas, código inalcanzable, pruebas sospechosas, variables no utilizadas y problemas de portabilidad. Trata las advertencias como indicaciones para inspeccionar el código, no como prueba automática de que el script está mal. A veces puedes deshabilitar intencionalmente una advertencia, pero agrega un breve comentario explicando por qué.
Usa trap para Ver la Línea que Falla
Para scripts más largos, una trampa de error puede indicarte dónde ocurrió un fallo:
set -Eeo pipefail
trap 'echo "Error en la línea $LINENO: $BASH_COMMAND" >&2' ERR
set -E ayuda a que la trampa ERR se propague a funciones y subcapas en Bash. Esto es útil en registros de CI donde puede que no tengas un shell interactivo.
Conclusión
Comienza con bash -n, usa bash -x o set -x dirigido para rastreo en tiempo de ejecución, y agrega registro enfocado en stderr alrededor de la rama que se comporta incorrectamente. Para scripts que importan, ejecuta ShellCheck y agrega una trampa ERR para que los fallos apunten al comando y la línea que necesitan atención.