Scripting Avanzado en Bash: Dominando Características del Shell para la Automatización
Aprende scripting avanzado en Bash con arreglos, sustitución de procesos, modo estricto, ShellCheck y expansión de parámetros para una automatización más segura.
Scripting Avanzado en Bash: Dominando Funciones del Shell para Automatización
El scripting en Bash se vuelve más difícil cuando tu script crece de unos pocos comandos a una automatización real. Necesitas un manejo de variables más seguro, entrada y salida más limpias, y menos archivos temporales.
Esta guía cubre funciones avanzadas de scripting en Bash que puedes usar en scripts de despliegue, verificaciones de registros y trabajos de mantenimiento. El objetivo no es un código shell ingenioso. Es código que puedas reejecutar, depurar y entregar a otro ingeniero.
1. Usa Arreglos de Bash para Listas Reales
Los arreglos te permiten almacenar múltiples valores sin dividir cadenas por espacios. Eso importa cuando los nombres de archivos, nombres de servicios o la entrada del usuario contienen espacios.
Arreglos Indexados
Los arreglos indexados son el tipo más común, donde los elementos se acceden mediante un índice numérico que comienza desde 0.
Ejemplo:
# Inicializar un arreglo indexado
COLORS=("rojo" "verde" "azul" "amarillo")
# Accediendo a elementos
echo "El segundo color es: ${COLORS[1]}"
# Agregando un elemento
COLORS+=( "morado" )
# Imprimiendo todos los elementos
echo "Todos los colores: ${COLORS[@]}"
Operaciones comunes con arreglos:
| Operación | Sintaxis | Descripción |
|---|---|---|
| Obtener Cantidad de Elementos | ${#ARRAY[@]} |
Devuelve el número total de elementos. |
| Obtener Longitud de un Elemento Específico | ${#ARRAY[indice]} |
Devuelve la longitud de la cadena en un índice específico. |
| Iteración | for item in "${ARRAY[@]}" |
Estructura de bucle estándar para procesar todos los elementos. |
Siempre cita las expansiones de arreglos con "${ARRAY[@]}". Eso mantiene "/var/log/mi app.log" como un solo argumento en lugar de dos.
Arreglos Asociativos
Los arreglos asociativos funcionan como pequeños mapas clave-valor. Requieren Bash 4 o posterior, así que verifica tus sistemas objetivo si soportas hosts macOS más antiguos.
Debes declarar un arreglo asociativo con -A:
# Declarar como arreglo asociativo
declare -A CONFIG_MAP
# Asignar pares clave-valor
CONFIG_MAP["puerto"]=8080
CONFIG_MAP["nombrehost"]="localhost"
CONFIG_MAP["tiempoespera"]=30
# Accediendo a valores
echo "Puerto configurado en: ${CONFIG_MAP["puerto"]}"
# Iterando sobre claves
for clave in "${!CONFIG_MAP[@]}"; do
echo "Clave: $clave, Valor: ${CONFIG_MAP[$clave]}"
done
2. Usa Sustitución de Procesos en Lugar de Archivos Temporales
La sustitución de procesos, escrita como <(comando) o >(comando), permite que un comando trate la salida de otro comando como un archivo. Es útil cuando una herramienta espera rutas de archivos pero tus datos provienen de comandos.
Cuándo Ayuda
Por ejemplo, supongamos que necesitas comparar dos listas de servicios generadas. diff espera rutas de archivos, pero no necesitas escribir esas listas en /tmp.
Sin sustitución de procesos:
# Ineficiente y desordenado
salida1=$(comando_a)
echo "$salida1" > /tmp/temp1.txt
salida2=$(comando_b)
echo "$salida2" > /tmp/temp2.txt
diff /tmp/temp1.txt /tmp/temp2.txt
rm /tmp/temp1.txt /tmp/temp2.txt
Comparación Directa Más Limpia
La sustitución de procesos le da a diff descriptores de archivos temporales y mantiene tu script más simple.
Con sustitución de procesos:
# Comparación limpia en una línea
diff <(comando_a) <(comando_b)
Este patrón funciona bien con comm, diff y herramientas que necesitan múltiples entradas de archivos.
Variaciones de Sintaxis:
<(comando)pasa la salida del comando a un lector.>(comando)envía la salida escrita a otro comando.
3. Agrega Modo Estricto y ShellCheck
El scripting avanzado en Bash debería fallar ruidosamente cuando ocurre algo inesperado. El modo estricto te ayuda a detectar variables faltantes y tuberías rotas antes de que causen daños silenciosos.
Opciones Esenciales del Modo Estricto
La mayoría de los scripts de automatización deberían comenzar con:
set -euo pipefail
-e: Salir cuando un comando falla.-u: Tratar las variables no establecidas como errores.-o pipefail: Hacer que una tubería falle si algún comando en la tubería falla.
Ejemplo:
# Sin pipefail, esto puede parecer exitoso porque wc sale limpiamente
cat archivo.log | grep patron_exitoso | wc -l
# Con set -o pipefail, el fallo de grep hace que la tubería falle.
Usa ShellCheck
ShellCheck detecta errores de citado, expansiones inseguras, código inalcanzable y problemas comunes de portabilidad.
Ejecútalo antes de confirmar un script:
shellcheck tu_script.sh
Cuando ShellCheck te pida que cites una variable o uses "${array[@]}", trátalo como un error real a menos que tengas una razón clara para ignorarlo.
4. Captura la Salida con Cuidado
La sustitución de comandos con $() es útil, pero puede ocultar fallos o mezclar flujos de salida si la usas casualmente.
Capturando Tanto STDOUT como STDERR
Cuando quieras registrar todo de un comando, captura tanto la salida estándar como el error estándar:
# Capturar tanto stdout como stderr en la VARIABLE
VARIABLE=$(comando_que_podria_fallar 2>&1)
# Descartar tanto stdout como stderr cuando solo necesitas el código de salida
comando_que_podria_fallar &> /dev/null
Expansión de Parámetros para Limpieza en Línea
La expansión de parámetros puede limpiar cadenas sin invocar sed o awk para casos simples.
${variable%patron}elimina el sufijo coincidente más corto.${variable%%patron}elimina el sufijo coincidente más largo.${variable#patron}elimina el prefijo coincidente más corto.${variable##patron}elimina el prefijo coincidente más largo.
Ejemplo:
ARCHIVO="reporte.log.bak"
# Eliminar el sufijo más corto que coincide con .bak
NOMBRE_LIMPIO=${ARCHIVO%.bak}
echo $NOMBRE_LIMPIO # Salida: reporte.log
# Eliminar todos los sufijos que coinciden con *.bak (solo elimina .bak aquí)
NOMBRE_LIMPIO_LARGO=${ARCHIVO%%.*}
echo $NOMBRE_LIMPIO_LARGO # Salida: reporte
Cuándo Pedir Ayuda
Pídele a un usuario de shell más experimentado que revise tu script cuando elimine archivos, cambie servicios de producción, maneje secretos o se ejecute desde CI. Bash es poderoso, pero un pequeño error de citado puede afectar cada archivo que coincida con un patrón.
Conclusión
Usa arreglos para listas reales, sustitución de procesos para salida de comandos similar a archivos, set -euo pipefail para fallos más seguros y ShellCheck para retroalimentación rápida. Esos hábitos hacen que el scripting avanzado en Bash sea más fácil de mantener y mucho menos sorprendente durante las ejecuciones de automatización.