CiberForja

Scripting en Bash: automatiza tareas en Linux

Aprende Bash scripting desde cero con enfoque práctico y empresarial. Variables, condicionales, bucles, funciones y ejemplos reales para administradores de sistemas Linux.

CCiberForja·5 de junio de 2026·15 min de lectura
Compartir:

Cualquier administrador de sistemas Linux con experiencia tiene un directorio lleno de scripts Bash que le ahorran horas cada semana. Scripts que hacen backups, que rotan logs, que monitorizan el espacio en disco, que despliegan aplicaciones, que crean usuarios en lote, que verifican el estado de servicios y envían alertas. Bash es el lenguaje nativo del sistema operativo Linux: no necesita intérpretes adicionales, está disponible en cualquier distribución, y puede hacer casi cualquier cosa que el sistema operativo sea capaz de hacer.

A pesar de su aparente simplicidad, Bash tiene una curva de aprendizaje que puede frustrar a quienes vienen de lenguajes más modernos. La sintaxis tiene sus peculiaridades (espacios que no puedes olvidar, comillas que importan, variables que pueden comportarse de forma inesperada si no las entrecomillas), pero una vez superada la fase inicial, es el lenguaje más eficiente para automatizar tareas de administración de sistemas Linux.

Esta guía abarca los conceptos fundamentales del scripting Bash con ejemplos prácticos orientados a entornos empresariales: desde la estructura básica de un script hasta técnicas avanzadas como el manejo robusto de errores, el procesamiento de argumentos, las funciones reutilizables y la integración con herramientas del sistema. Al terminar, tendrás las bases sólidas para escribir scripts que funcionen de forma fiable en producción.

Estructura básica de un script Bash

Todo script Bash empieza con la línea shebang: #!/bin/bash (o #!/usr/bin/env bash para mayor portabilidad). Esta línea indica al sistema operativo qué intérprete usar cuando se ejecuta el script directamente. A continuación, una buena práctica es incluir las opciones de modo estricto: set -euo pipefail. La opción -e hace que el script se detenga en el primer error; -u trata las variables no definidas como error; y pipefail hace que un pipe falle si cualquier comando en la cadena falla, no solo el último.

El modo estricto (set -euo pipefail) es probablemente la medida individual más importante para escribir scripts de producción seguros. Sin -e, un script puede continuar ejecutándose después de un error crítico, potencialmente causando daños mayores. Sin -u, un typo en el nombre de una variable produce silenciosamente una cadena vacía que puede tener consecuencias desastrosas (imagina rm -rf $DIRECTORIO/ donde $DIRECTORIO está vacío).

Variables, tipos y ámbito

En Bash, las variables se asignan sin espacios alrededor del signo igual: NOMBRE=valor. Para acceder a su valor se usa el símbolo $: echo $NOMBRE o, más correctamente, echo "$NOMBRE" (siempre entre comillas dobles para evitar word splitting con valores que contienen espacios). Las variables en Bash son globales por defecto dentro del script; para declarar variables locales dentro de una función se usa la palabra clave local.

Bash no tiene tipos de datos en sentido estricto: todo son cadenas de texto. Para operaciones aritméticas se usa la sintaxis $(( )) o el comando let. Los arrays se declaran con ARRAY=(elem1 elem2 elem3) y se accede a sus elementos con ${ARRAY[0]}, ${ARRAY[@]} para todos los elementos, y ${#ARRAY[@]} para el número de elementos. Los arrays asociativos (diccionarios) están disponibles en Bash 4+ con declare -A.

Condicionales y comparaciones

La estructura condicional básica en Bash es if [ condición ]; then ... elif [ condición ]; then ... else ... fi. Los corchetes [ ] son en realidad el comando test, y la doble corchete [[ ]] es una versión mejorada específica de Bash que soporta más operadores y maneja mejor las cadenas vacías y los caracteres especiales. Para comparaciones numéricas se usan -eq, -ne, -lt, -le, -gt, -ge. Para cadenas: = y != (con []) o == y != (con [[]]).

  • [ -f archivo ]: verdadero si el archivo existe y es un fichero regular.
  • [ -d directorio ]: verdadero si el directorio existe.
  • [ -z "$VAR" ]: verdadero si la variable está vacía.
  • [ -n "$VAR" ]: verdadero si la variable no está vacía.
  • [ -r archivo ]: verdadero si el archivo existe y es legible.
  • [ $A -eq $B ]: verdadero si A es igual a B (numérico).
  • [[ $CADENA =~ ^[0-9]+$ ]]: verdadero si la cadena contiene solo dígitos (regex).

Bucles: for, while y until

Los bucles son esenciales para procesar colecciones de archivos, líneas de texto, usuarios o cualquier otra secuencia de elementos. El bucle for en Bash tiene varias formas: for item in lista; do ... done para iterar sobre una lista, for i in $(seq 1 10); do ... done para iterar sobre un rango numérico, y for (( i=0; i<10; i++ )); do ... done para un bucle estilo C.

El bucle while es ideal para leer archivos línea a línea: while IFS= read -r linea; do ... done < archivo.txt. La construcción IFS= read -r es la forma correcta de leer líneas preservando espacios y barras invertidas. El bucle until funciona como while pero continúa mientras la condición sea falsa, útil para esperar a que un servicio esté disponible: until curl -s http://localhost:8080/health; do sleep 2; done.

Funciones: reutilización y organización del código

Las funciones en Bash se declaran con nombre_funcion() { ... } o con function nombre_funcion { ... }. Los argumentos se acceden con $1, $2, etc. (igual que los argumentos del script). Las funciones no tienen valores de retorno en el sentido tradicional: return solo devuelve un código de salida (0-255). Para devolver un valor de texto, la función lo imprime con echo o printf y el llamador lo captura con sustitución de comando: RESULTADO=$(mi_funcion).

Organizar los scripts largos en funciones tiene múltiples beneficios: hace el código más legible, facilita el testing de partes individuales, permite reutilizar lógica y hace que el flujo principal del script sea una secuencia de llamadas a funciones que puede entenderse de un vistazo. Un script bien estructurado tiene una función main() al final que llama a las demás funciones en orden, y el cuerpo del script solo contiene la llamada main "$@".

Manejo de errores robusto

Con set -e activado, cualquier comando que devuelva un código de salida distinto de cero detiene el script. Pero a veces quieres capturar y gestionar los errores tú mismo. El operador || permite ejecutar un comando alternativo si el anterior falla: comando_principal || comando_de_fallback. Para capturar el código de salida de un comando específico sin que -e detenga el script, puedes usar comando || true o guardar el código con: comando; CODIGO=$?.

Una técnica fundamental para scripts de producción es el trap: permite ejecutar una función de limpieza cuando el script termina, ya sea de forma normal, por error o por una señal del sistema (Ctrl+C). El patrón típico es: trap cleanup EXIT, donde cleanup es una función que elimina archivos temporales, libera recursos o registra que el script terminó inesperadamente. Esto garantiza que los scripts no dejen rastros aunque fallen.

Procesamiento de argumentos con getopts

Los scripts profesionales aceptan argumentos con opciones al estilo Unix: -f archivo, -v para modo verbose, -h para mostrar ayuda. getopts es el comando nativo de Bash para procesar estos argumentos de forma estándar. Se usa en un bucle while: while getopts ':f:vh' opcion; do case $opcion in f) ARCHIVO=$OPTARG ;; v) VERBOSE=true ;; h) mostrar_ayuda ;; *) echo 'Opción desconocida' ; exit 1 ;; esac; done.

La colon inicial en la cadena de opciones (:f:vh) activa el modo silencioso que permite gestionar tú mismo los errores. Los dos puntos después de una letra (f:) indican que esa opción requiere un argumento. Esta estructura hace que tus scripts sean más usables, documentados y fáciles de integrar en otros scripts o en pipelines de automatización.

Manipulación de texto: sed, awk y las herramientas Unix

Bash brilla especialmente cuando se combina con las herramientas clásicas de manipulación de texto de Unix. grep filtra líneas que coinciden con un patrón. sed edita texto mediante sustituciones y transformaciones. awk procesa texto estructurado por campos (ideal para CSVs, logs con delimitadores, salidas de comandos). cut extrae columnas. sort ordena. uniq elimina duplicados. tr transliteral caracteres. wc cuenta líneas, palabras o caracteres.

La verdadera potencia aparece al encadenar estas herramientas con pipes. Por ejemplo, para encontrar los diez procesos que más memoria consumen: ps aux --sort=-%mem | awk '{print $2, $4, $11}' | head -10. O para extraer todas las IPs únicas de un log de acceso de NGINX: awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20. Estos one-liners pueden guardarse en scripts y ejecutarse periódicamente para monitorización.

Scripts de administración de sistemas: ejemplos reales

Script de backup con rotación

Un script de backup robusto debe: crear un directorio de destino con fecha y hora en el nombre, hacer una copia comprimida con tar o rsync, verificar que la copia se creó correctamente comprobando el código de salida y el tamaño del archivo resultante, eliminar backups más antiguos de N días con find -mtime +N -delete, y registrar el resultado en un archivo de log con timestamp. Este script, programado con cron o systemd timer, proporciona una solución de backup básica pero fiable para servidores sin costes de software adicional.

Script de monitorización de espacio en disco

Un script que comprueba el uso de disco en todos los sistemas de archivos montados y envía una alerta por correo o Slack cuando alguno supera un umbral (por ejemplo, 85%) puede ejecutarse cada hora via cron y detectar problemas antes de que el disco lleno cause fallos en la aplicación. Usa df -h para obtener el uso, awk para extraer el porcentaje, comparaciones numéricas para verificar el umbral, y curl con la API de Slack para enviar la notificación.

Script de gestión de usuarios en lote

Crear decenas de usuarios con sus grupos, directorios home y permisos correctos es una tarea tediosa pero perfectamente automatizable. Un script que lee un CSV con nombre, apellido, departamento y rol, crea el usuario con useradd, asigna la contraseña inicial aleatoria con openssl rand, añade al usuario a los grupos correspondientes y envía las credenciales por correo puede procesar cincuenta usuarios en el tiempo que manualmente se tardaría en crear uno.

Depuración de scripts Bash

Los scripts Bash pueden ser difíciles de depurar si no conoces las herramientas adecuadas. El modo de depuración (bash -x script.sh o añadir set -x en el script) imprime cada comando antes de ejecutarlo, mostrando cómo se expanden las variables. Es el primer recurso cuando un script no se comporta como esperas. Para depuración más selectiva, puedes activar y desactivar set -x en secciones específicas del script.

La herramienta shellcheck (disponible en repositorios de todas las distribuciones y como extensión de VS Code) analiza estáticamente los scripts Bash y detecta errores comunes: variables sin entrecomillar, comparaciones incorrectas, uso de funciones deprecated, posibles problemas de seguridad. Es el equivalente a un linter de código para Bash y debería ser el primer paso antes de ejecutar cualquier script nuevo en producción.

Integración con cron y systemd timers

Los scripts de automatización cobran todo su valor cuando se ejecutan automáticamente en un horario. Cron es el planificador clásico de Unix, disponible en todas las distribuciones. La tabla de cron del usuario (crontab -e) permite definir trabajos con la sintaxis: minuto hora día mes día_semana /ruta/al/script.sh. Una práctica importante es redirigir la salida del script a un log: */5 * * * * /opt/scripts/check_disk.sh >> /var/log/check_disk.log 2>&1.

Systemd timers son la alternativa moderna a cron con ventajas importantes: las salidas de los scripts se integran con journald (consultables con journalctl), pueden depender de otros servicios systemd, soportan retrasos aleatorios para distribuir la carga, y ofrecen más control sobre el entorno de ejecución. Para scripts de producción en distribuciones modernas como RHEL 8+, Ubuntu 20.04+ o Debian 11+, los timers de systemd son la opción recomendada.

Un buen script de Bash no es el que hace la tarea, sino el que la hace de forma predecible, registra lo que hace, gestiona correctamente los errores y puede ejecutarse sin supervisión en producción durante meses sin sorpresas.

Buenas prácticas de seguridad en scripts

  • Nunca uses eval con datos de entrada del usuario: es la puerta abierta a la inyección de comandos.
  • Valida y sanitiza todos los argumentos y datos externos antes de usarlos en comandos del sistema.
  • Usa rutas absolutas para los comandos en scripts que se ejecutan con privilegios elevados (sudo o cron de root).
  • Protege los archivos de script con permisos apropiados: chmod 750 para scripts con datos sensibles.
  • No almacenes contraseñas en scripts: usa variables de entorno, archivos de credenciales con permisos restrictivos o gestores de secretos.
  • Ejecuta los scripts con el mínimo privilegio necesario: no uses sudo o root si el script no lo necesita.

Conclusión: Bash como inversión en productividad a largo plazo

Invertir tiempo en aprender Bash scripting es una de las decisiones con mayor retorno para cualquier profesional de sistemas Linux. Cada script que escribes es trabajo que no tendrás que volver a hacer manualmente: el sistema lo hará por ti, de forma más rápida, más consistente y sin errores de distracción. Los scripts maduros se convierten en el conocimiento operativo de tu equipo, documentado en código y ejecutable por cualquier miembro.

La recomendación es empezar por la tarea manual que más tiempo te consume cada semana y convertirla en un script. No tiene que ser perfecto: funcional es suficiente para empezar. Con el tiempo, a medida que el script se usa y se mejora, irá adquiriendo robustez, manejo de errores y documentación. Ese proceso iterativo es exactamente cómo nacen los scripts de producción fiables.

¿Te ha servido? Compártelo

Compartir:
C
Escrito por
CiberForja

Consultor TI. Especializado en sistemas, redes y ciberseguridad.

Más sobre nosotros →

Comentarios

Sé el primero en comentar.

Deja tu comentario

Sigue leyendo