Por qué aprender Bash sigue siendo una inversión rentable
En casi cualquier entorno Linux hay tareas que se repiten: limpiar directorios temporales, respaldar una base de datos, rotar registros, revisar el espacio en disco o desplegar una nueva versión de una aplicación. Hacerlas a mano una vez es tolerable; hacerlas a mano todos los días es una fuente segura de errores y de tiempo perdido. Bash es la herramienta que la mayoría de los administradores y desarrolladores tiene más a mano para convertir esos procedimientos en programas pequeños, legibles y repetibles.
Un script de Bash no es más que un archivo de texto con una secuencia de comandos que normalmente escribirías en la terminal, enriquecida con variables, condiciones, bucles y funciones. Esa cercanía con la terminal es su mayor ventaja: todo lo que ya sabes hacer de forma interactiva puede pasar a un script casi sin cambios. Además, Bash está instalado por defecto en la inmensa mayoría de distribuciones, en contenedores basados en Debian o Ubuntu, en servidores en la nube y en el Subsistema de Windows para Linux.
La versión estable actual del intérprete es GNU Bash 5.3, publicada en julio de 2025. Todo lo que verás en esta guía funciona también en versiones 4.x y 5.x anteriores, que son las que todavía encuentras en muchas distribuciones con soporte extendido. Cuando un recurso sea exclusivo de las versiones recientes, se indicará de forma explícita.
El recorrido va desde lo más básico, como ejecutar comandos y crear el primer archivo, hasta temas que marcan la diferencia en producción: manejo de argumentos, control de errores, programación con cron y técnicas de depuración. Cada sección incluye ejemplos propios que puedes copiar y adaptar.
Shell, terminal y Bash: aclarando los términos
Antes de escribir código conviene separar tres conceptos que a menudo se usan como sinónimos.
La terminal (o emulador de terminal) es la ventana donde escribes: GNOME Terminal, Konsole, Windows Terminal o la consola integrada de tu editor. Su trabajo es mostrar texto y enviar lo que tecleas a un programa.
La shell es ese programa: un intérprete que recibe órdenes, las analiza y las ejecuta. Hay varias: sh (la shell POSIX básica), Bash, Zsh, Fish, Dash o Ksh. Cada una tiene su sintaxis y sus extensiones, aunque todas comparten un núcleo común definido por el estándar POSIX.
Bash, acrónimo de Bourne Again Shell, es la shell del proyecto GNU y la predeterminada en la mayoría de distribuciones Linux para usuarios interactivos. macOS cambió su shell por defecto a Zsh hace años, y Debian y Ubuntu usan Dash como /bin/sh para los scripts del sistema por razones de velocidad. Esto tiene una consecuencia práctica importante: un script que usa características propias de Bash debe ejecutarse con Bash, no con sh, o fallará de formas difíciles de entender.
Puedes averiguar qué shell estás usando y qué versión de Bash tienes con dos comandos:
echo "$SHELL"
bash --version | head -n 1
/bin/bash
GNU bash, version 5.3.0(1)-release (x86_64-pc-linux-gnu)
Preparar el entorno de trabajo
Si ya usas Linux, no necesitas instalar nada. En Windows 10 y 11 la vía más cómoda es el Subsistema de Windows para Linux, que se instala con un solo comando desde una terminal de PowerShell con permisos de administrador:
wsl --install
Tras reiniciar, tendrás una distribución Ubuntu con Bash lista para usar. En macOS, Bash viene instalado pero en una versión muy antigua por cuestiones de licencia; si quieres una versión moderna puedes instalarla con Homebrew:
brew install bash
Otra alternativa sin instalación es usar un contenedor desechable, útil para practicar sin tocar tu sistema:
docker run --rm -it ubuntu:latest bash
Para escribir scripts, cualquier editor sirve. Visual Studio Code con la extensión de ShellCheck, Vim o Nano son opciones habituales. Lo importante es guardar los archivos con finales de línea Unix (LF): un script guardado con finales de Windows (CRLF) produce errores extraños que veremos en la sección de problemas frecuentes.
Primeros comandos en la terminal
Un script es una colección de comandos, así que conviene sentirse cómodo con los más habituales. Estos son los que aparecerán una y otra vez:
date # fecha y hora actuales
pwd # directorio de trabajo actual
ls -lh # contenido del directorio con tamaños legibles
echo "Hola, mundo" # imprime un texto
whoami # usuario actual
sáb 31 oct 2026 09:41:28 CST
/home/ana/proyectos
total 12K
drwxr-xr-x 2 ana ana 4,0K oct 31 09:12 informes
-rw-r--r-- 1 ana ana 512 oct 31 09:30 notas.txt
Hola, mundo
ana
La tabla siguiente reúne los comandos que más se usan dentro de scripts de automatización:
| Comando | Propósito |
|---|---|
cd | Cambiar de directorio |
mkdir -p | Crear directorios, incluidos los intermedios |
cp, mv, rm | Copiar, mover y borrar archivos |
cat, less, head, tail | Ver contenido de archivos |
grep | Buscar texto con patrones |
find | Localizar archivos por nombre, fecha o tamaño |
chmod, chown | Cambiar permisos y propietario |
ps, kill | Ver y terminar procesos |
df, du | Espacio en disco y tamaño de directorios |
tar | Empaquetar y comprimir |
curl | Hacer peticiones HTTP |
Cuando dudes sobre las opciones de un comando, consulta su manual con man, o la ayuda rápida con --help:
man find
grep --help | head -n 20
Tu primer script: estructura mínima
Crea un archivo llamado resumen.sh. La extensión .sh no es obligatoria para el sistema, pero es una convención que ayuda a identificar el archivo de un vistazo.
#!/usr/bin/env bash
# Muestra un resumen del directorio indicado por el usuario
read -rp "Directorio a revisar: " directorio
echo "Contenido de $directorio:"
ls -lh -- "$directorio"
La primera línea es el shebang. Le dice al sistema qué intérprete debe ejecutar el archivo. Es habitual ver #!/bin/bash, que funciona en casi todas las distribuciones Linux, pero #!/usr/bin/env bash es más portable porque busca Bash en el PATH, lo que resuelve el caso de macOS con Homebrew o de sistemas BSD donde Bash no vive en /bin.
Las líneas que empiezan por # (excepto el shebang) son comentarios y el intérprete las ignora.
Permisos de ejecución y formas de lanzar un script
Un archivo recién creado no tiene permiso de ejecución. Puedes comprobarlo y corregirlo así:
ls -l resumen.sh
chmod u+x resumen.sh
ls -l resumen.sh
-rw-r--r-- 1 ana ana 142 oct 31 09:45 resumen.sh
-rwxr--r-- 1 ana ana 142 oct 31 09:45 resumen.sh
La x que aparece en el segundo listado indica que el propietario ya puede ejecutarlo. Hay tres formas habituales de lanzarlo:
./resumen.sh # usa el shebang; requiere permiso de ejecución
bash resumen.sh # fuerza Bash; no requiere permiso de ejecución
source resumen.sh # ejecuta en la shell actual; las variables persisten
Evita sh resumen.sh si el script usa características de Bash. En Debian y Ubuntu sh apunta a Dash, que no entiende construcciones como [[ ]] o los arreglos.
La diferencia con source también merece atención: los dos primeros métodos crean un proceso nuevo, de modo que cualquier variable o cambio de directorio desaparece al terminar. Con source (o su abreviatura .) el script se ejecuta dentro de tu sesión, lo que es útil para cargar configuraciones, pero peligroso si el script incluye un exit, porque cerrará tu terminal.
Variables: asignación, expansión y comillas
En Bash todas las variables son, en esencia, cadenas de texto. No hay declaración de tipos, aunque el intérprete sabe operar aritméticamente cuando se lo pides de forma explícita.
proyecto="inventario"
version=3
ruta_logs="/var/log/$proyecto"
echo "Proyecto: $proyecto, versión $version"
echo "Registros en: ${ruta_logs}/app.log"
Proyecto: inventario, versión 3
Registros en: /var/log/inventario/app.log
Hay tres reglas que evitan la mayoría de los errores de principiante:
- No pongas espacios alrededor del signo igual.
nombre = "x"se interpreta como ejecutar un comando llamadonombrecon dos argumentos. - Usa llaves cuando la variable va pegada a otro texto:
${ruta_logs}_oldy no$ruta_logs_old, que buscaría una variable distinta. - Encierra las expansiones entre comillas dobles. Sin comillas, un valor con espacios se divide en varias palabras y los comodines se expanden.
Los nombres de variables pueden contener letras, números y guiones bajos, pero no pueden empezar con un número. Por convención, las variables de entorno y las constantes van en mayúsculas (PATH, HOME, MAX_REINTENTOS) y las variables locales en minúsculas, lo que reduce el riesgo de sobrescribir por accidente una variable del sistema.
La diferencia entre comillas dobles y simples es fundamental: las dobles permiten expandir variables, las simples conservan el texto literal.
usuario="ana"
echo "Hola, $usuario"
echo 'Hola, $usuario'
Hola, ana
Hola, $usuario
Para capturar la salida de un comando en una variable se usa la sustitución de comandos, y para operar con números, la expansión aritmética:
fecha=$(date +%F)
archivos=$(ls | wc -l)
total=$(( archivos * 2 + 1 ))
echo "Hoy es $fecha y hay $archivos archivos (cálculo: $total)"
Bash 5.3 introdujo una variante de sustitución de comandos, ${ comando; }, que ejecuta el comando en la shell actual en lugar de crear un subproceso. Es más rápida en bucles intensivos, pero si tu script debe funcionar en servidores con versiones anteriores, sigue usando $( ).
Recibir datos: entrada interactiva, archivos y argumentos
Un script útil casi siempre necesita información externa. Hay tres fuentes principales.
La primera es la entrada interactiva con read. La opción -r evita que las barras invertidas se interpreten y -p muestra un mensaje:
read -rp "Nombre del servidor: " servidor
read -rsp "Contraseña: " clave
echo
echo "Conectando a $servidor..."
La opción -s oculta lo que se escribe, útil para contraseñas.
La segunda es leer un archivo línea por línea. El patrón correcto combina while con read y redirección de entrada:
#!/usr/bin/env bash
# Comprueba si cada servidor de la lista responde
while IFS= read -r host; do
[[ -z "$host" || "$host" == \#* ]] && continue
if ping -c 1 -W 2 "$host" &> /dev/null; then
echo "OK $host"
else
echo "FALLO $host"
fi
done < servidores.txt
El IFS= vacío conserva los espacios al principio y al final de cada línea, y la condición inicial salta líneas vacías y comentarios.
La tercera, y la más usada en automatización, son los argumentos posicionales. Bash los expone en variables especiales:
| Variable | Contenido |
|---|---|
$0 | Nombre del script |
$1, $2, … | Primer, segundo argumento, etc. |
$# | Número de argumentos |
$@ | Todos los argumentos, cada uno como palabra independiente |
$? | Código de salida del último comando |
$$ | PID del proceso actual |
Un script que recibe un directorio y una extensión podría verse así:
#!/usr/bin/env bash
# Uso: ./contar.sh <directorio> <extension>
if (( $# != 2 )); then
echo "Uso: $0 <directorio> <extension>" >&2
exit 1
fi
directorio=$1
extension=$2
cantidad=$(find "$directorio" -type f -name "*.${extension}" | wc -l)
echo "Hay $cantidad archivos .$extension en $directorio"
$ ./contar.sh ~/proyectos py
Hay 47 archivos .py en /home/ana/proyectos
Validar el número de argumentos al principio y mostrar un mensaje de uso en la salida de error (>&2) es una práctica que distingue un script improvisado de una herramienta que otros pueden usar sin leer su código.
Escribir resultados: salida estándar, errores y redirecciones
echo es la forma más directa de producir salida, pero printf ofrece control preciso sobre el formato y se comporta igual en todas las shells:
printf "%-12s %8s\n" "Servicio" "Estado"
printf "%-12s %8s\n" "nginx" "activo"
printf "%-12s %8s\n" "postgresql" "detenido"
Servicio Estado
nginx activo
postgresql detenido
Las redirecciones permiten enviar esa salida a archivos en lugar de a la pantalla:
df -h > espacio.txt # sobrescribe el archivo
date >> historial.log # añade al final
comando_inexistente 2> errores.log # solo la salida de error
./respaldo.sh &> respaldo.log # salida estándar y error juntas
Cada proceso tiene tres canales: la entrada estándar (0), la salida estándar (1) y la salida de error (2). Separar mensajes normales de errores permite, por ejemplo, guardar los resultados en un archivo mientras los problemas siguen apareciendo en pantalla. Para descartar por completo una salida, se redirige a /dev/null.
Las tuberías (|) conectan la salida de un comando con la entrada del siguiente, y son la base de buena parte de la potencia de la línea de comandos de Linux:
# Las cinco direcciones IP con más peticiones en un log de acceso
cut -d' ' -f1 access.log | sort | uniq -c | sort -rn | head -n 5
Condicionales: tomar decisiones en el script
La estructura if evalúa una condición y ejecuta un bloque si se cumple. En Bash moderno conviene usar [[ ]] para comparaciones de texto y archivos, y (( )) para comparaciones numéricas:
#!/usr/bin/env bash
# Alerta según el porcentaje de uso del disco raíz
uso=$(df --output=pcent / | tail -n 1 | tr -dc '0-9')
if (( uso >= 90 )); then
echo "CRÍTICO: disco al ${uso}%"
elif (( uso >= 75 )); then
echo "AVISO: disco al ${uso}%"
else
echo "Normal: disco al ${uso}%"
fi
Estas son las comprobaciones más habituales dentro de [[ ]]:
| Expresión | Verdadera cuando |
|---|---|
-f archivo | Existe y es un archivo regular |
-d ruta | Existe y es un directorio |
-x archivo | Existe y es ejecutable |
-z "$var" | La cadena está vacía |
-n "$var" | La cadena no está vacía |
"$a" == "$b" | Las cadenas son iguales |
"$a" == patron* | La cadena coincide con el patrón |
"$a" =~ regex | La cadena coincide con la expresión regular |
Para combinar condiciones usa && y ||. Muchos tutoriales antiguos utilizan -a y -o dentro de corchetes simples, pero el estándar POSIX los considera obsoletos porque producen ambigüedades; dentro de [[ ]] directamente no se admiten.
if [[ -f "$config" && -r "$config" ]]; then
echo "Cargando configuración"
fi
if (( hora < 8 || hora > 20 )); then
echo "Fuera de horario laboral"
fi
Para comparaciones numéricas con corchetes también existen los operadores -eq, -ne, -lt, -le, -gt y -ge, que verás en scripts antiguos y que siguen siendo válidos y portables a sh.
Bucles para repetir trabajo
El bucle for recorre una lista de elementos, que puede ser un rango, una lista literal o el resultado de un comodín:
for numero in {1..5}; do
echo "Iteración $numero"
done
for entorno in desarrollo pruebas produccion; do
echo "Desplegando en $entorno"
done
for imagen in fotos/*.jpg; do
echo "Procesando $imagen"
done
También existe una forma con estilo de C, útil cuando necesitas un contador con paso personalizado:
for (( i = 0; i <= 100; i += 25 )); do
echo "Progreso: $i%"
done
El bucle while se repite mientras la condición sea verdadera. Es la elección natural para reintentos o esperas:
#!/usr/bin/env bash
# Espera hasta que un servicio web responda, con un límite de intentos
url="http://localhost:8080/salud"
intento=1
max_intentos=10
while (( intento <= max_intentos )); do
if curl -fsS "$url" > /dev/null; then
echo "Servicio disponible tras $intento intento(s)"
exit 0
fi
echo "Intento $intento fallido, reintentando en 3 s..."
sleep 3
(( intento++ ))
done
echo "El servicio no respondió" >&2
exit 1
Dentro de cualquier bucle, break sale inmediatamente y continue salta a la siguiente iteración.
Selección múltiple con case
Cuando una variable puede tomar varios valores y cada uno exige una acción distinta, case es más legible que una cadena de elif. Es el patrón clásico para scripts que aceptan subcomandos:
#!/usr/bin/env bash
# Gestor sencillo para una aplicación
accion=${1:-ayuda}
case "$accion" in
iniciar)
echo "Iniciando aplicación..."
;;
detener)
echo "Deteniendo aplicación..."
;;
reiniciar|restart)
echo "Reiniciando aplicación..."
;;
estado)
echo "La aplicación está en ejecución"
;;
*)
echo "Uso: $0 {iniciar|detener|reiniciar|estado}"
exit 1
;;
esac
Cada patrón termina en ), cada bloque en ;;, y el asterisco actúa como opción por defecto. La barra vertical permite que varios valores compartan la misma acción. La expresión ${1:-ayuda} asigna un valor predeterminado si no se pasa ningún argumento.
Funciones para organizar el código
A medida que un script crece, agrupar la lógica en funciones lo hace más fácil de leer y de probar. Las funciones de Bash reciben argumentos igual que un script, en $1, $2, y devuelven un código de salida con return:
#!/usr/bin/env bash
log() {
local nivel=$1
shift
printf '%s [%s] %s\n' "$(date '+%F %T')" "$nivel" "$*"
}
respaldar_directorio() {
local origen=$1
local destino=$2
local nombre
nombre="$(basename "$origen")-$(date +%F).tar.gz"
if [[ ! -d "$origen" ]]; then
log ERROR "No existe el directorio $origen"
return 1
fi
tar -czf "$destino/$nombre" -C "$(dirname "$origen")" "$(basename "$origen")"
log INFO "Respaldo creado: $destino/$nombre"
}
respaldar_directorio "$HOME/documentos" "/srv/respaldos"
2026-10-31 09:58:12 [INFO] Respaldo creado: /srv/respaldos/documentos-2026-10-31.tar.gz
La palabra local limita la variable a la función, evitando que sobrescriba otra con el mismo nombre en el resto del script. Es un hábito sencillo que evita errores muy difíciles de rastrear.
Programar tareas con cron
Escribir un script es solo la mitad del trabajo de automatización; la otra mitad es que se ejecute solo. Cron es el planificador clásico de Unix y está disponible en prácticamente cualquier servidor Linux.
Cada usuario tiene su propia tabla de tareas, que se edita y consulta con el comando crontab en Linux de la siguiente forma:
crontab -e # editar las tareas del usuario actual
crontab -l # listar las tareas programadas
Cada línea de la tabla tiene cinco campos de tiempo seguidos del comando:
# ┌─ minuto (0-59)
# │ ┌─ hora (0-23)
# │ │ ┌─ día del mes (1-31)
# │ │ │ ┌─ mes (1-12)
# │ │ │ │ ┌─ día de la semana (0-6, domingo = 0)
# │ │ │ │ │
0 2 * * * /home/ana/scripts/respaldo.sh >> /home/ana/logs/respaldo.log 2>&1
Algunos ejemplos de programación habituales:
| Expresión | Significado |
|---|---|
*/15 * * * * | Cada 15 minutos |
0 * * * * | Al inicio de cada hora |
30 6 * * 1-5 | A las 6:30, de lunes a viernes |
0 0 1 * * | A medianoche del primer día de cada mes |
0 3 * * 0 | Los domingos a las 3:00 |
@reboot | Una vez, al arrancar el sistema |
Hay tres detalles que causan la mayoría de las tareas que “no se ejecutan”. Cron usa un entorno mínimo, con un PATH reducido, así que conviene usar rutas absolutas para scripts y comandos. Cron no muestra la salida en ninguna parte, de modo que redirigirla a un archivo de registro es imprescindible para saber qué pasó. Y el script debe tener permiso de ejecución y un shebang correcto.
En distribuciones modernas basadas en systemd, los temporizadores de systemd (systemd timers) son una alternativa a cron con ventajas como el registro integrado en el journal y la ejecución de tareas perdidas si el equipo estaba apagado. Para tareas sencillas, cron sigue siendo la opción más directa y universal.
Depuración y manejo de errores
Por defecto, Bash continúa ejecutando el script aunque un comando falle, lo que puede tener consecuencias graves: si un cd falla y la siguiente línea es un rm, el borrado ocurre en el directorio equivocado. Por eso muchos scripts profesionales empiezan con una cabecera que activa un modo más estricto:
#!/usr/bin/env bash
set -euo pipefail
Cada opción cumple un papel distinto:
set -etermina el script cuando un comando devuelve un código distinto de cero.set -utrata como error el uso de variables no definidas, lo que detecta erratas en nombres.set -o pipefailhace que una tubería falle si falla cualquiera de sus comandos, no solo el último.
Este modo no es infalible: set -e tiene excepciones, por ejemplo dentro de condiciones de if o en comandos encadenados con ||. Aun así, reduce de forma notable los fallos silenciosos.
Para ver exactamente qué ejecuta el intérprete, activa el modo de traza con set -x. Cada comando se imprime con sus variables ya expandidas, precedido de un signo más:
#!/usr/bin/env bash
set -x
destino="/tmp/prueba"
mkdir -p "$destino"
cp notas.txt "$destino/"
+ destino=/tmp/prueba
+ mkdir -p /tmp/prueba
+ cp notas.txt /tmp/prueba/
Puedes activar la traza solo en una parte del script con set -x y desactivarla con set +x, o lanzar el script completo en modo traza con bash -x script.sh.
El código de salida de cada comando está disponible en $?. Un cero significa éxito y cualquier otro valor indica un error:
grep -q "ERROR" app.log
echo "Código de salida: $?"
Otra herramienta muy útil es trap, que ejecuta un bloque cuando el script termina o recibe una señal. Es la forma correcta de limpiar archivos temporales pase lo que pase:
#!/usr/bin/env bash
set -euo pipefail
temporal=$(mktemp)
trap 'rm -f "$temporal"' EXIT
curl -fsS "https://ejemplo.com/datos.csv" -o "$temporal"
wc -l "$temporal"
Finalmente, para tareas programadas con cron, revisa los registros del sistema. En Ubuntu y Debian puedes buscar las ejecuciones en el journal o en el archivo de syslog:
journalctl -u cron --since "today"
grep CRON /var/log/syslog | tail -n 20
Errores frecuentes y cómo resolverlos
Algunos mensajes de error aparecen con tanta frecuencia que vale la pena reconocerlos de inmediato.
Permiso denegado al ejecutar el script. Falta el permiso de ejecución:
bash: ./respaldo.sh: Permission denied
Se resuelve con chmod u+x respaldo.sh, o ejecutando con bash respaldo.sh.
Comando no encontrado al asignar una variable. Casi siempre es un espacio alrededor del signo igual:
./script.sh: line 3: nombre: command not found
Intérprete incorrecto con caracteres extraños. El archivo tiene finales de línea de Windows:
/usr/bin/env: 'bash\r': No such file or directory
Conviértelo con dos2unix script.sh o con sed -i 's/\r$//' script.sh, y configura tu editor para guardar con LF.
Sintaxis no reconocida al ejecutar con sh. Aparece cuando un script de Bash se lanza con Dash:
script.sh: 5: [[: not found
Ejecuta el script con bash o con ./script.sh respetando el shebang.
Argumentos divididos inesperadamente. Una variable sin comillas que contiene espacios se convierte en varios argumentos:
archivo="informe final.pdf"
rm $archivo # intenta borrar "informe" y "final.pdf"
rm "$archivo" # borra el archivo correcto
Para detectar este tipo de problemas antes de que ocurran, ShellCheck es la herramienta de referencia. Analiza el script de forma estática y explica cada advertencia:
sudo apt install shellcheck
shellcheck respaldo.sh
In respaldo.sh line 7:
rm $archivo
^------^ SC2086 (info): Double quote to prevent globbing and word splitting.
Buenas prácticas para scripts en producción
Las siguientes recomendaciones convierten un script que funciona en tu máquina en uno que puede confiarse a un servidor:
- Empieza siempre con el shebang y
set -euo pipefail, salvo que tengas una razón concreta para no hacerlo. - Entrecomilla todas las expansiones de variables y sustituciones de comandos.
- Valida argumentos y dependencias al principio, con mensajes de uso claros.
- Usa rutas absolutas en scripts que ejecutará cron u otro proceso automático.
- Envía los mensajes de error a la salida de error y devuelve códigos de salida significativos.
- Usa
localen las funciones y nombres de variables descriptivos. - Limpia los recursos temporales con
trap. - No guardes contraseñas en el propio script; léelas de variables de entorno o de un gestor de secretos.
- Pasa ShellCheck antes de cada cambio y versiona los scripts con Git.
Cuando un proyecto acumula varios scripts, una estructura ordenada facilita mantenerlos y reutilizar funciones comunes:
automatizacion/
├── bin/
│ ├── respaldo.sh
│ ├── limpieza.sh
│ └── monitor-disco.sh
├── lib/
│ └── comun.sh
├── config/
│ └── servidores.txt
├── logs/
└── README.md
Las funciones compartidas, como la de registro, viven en lib/comun.sh y cada script las carga con source:
#!/usr/bin/env bash
set -euo pipefail
directorio_base="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
source "$directorio_base/lib/comun.sh"
log INFO "Iniciando limpieza de temporales"
find /tmp -type f -mtime +7 -delete
log INFO "Limpieza completada"
Cuándo Bash no es la herramienta adecuada
Saber cuándo no usar Bash es tan importante como saber escribirlo. Para scripts de Bash en Linux que encadenan comandos del sistema, mueven archivos o lanzan procesos, es difícil superar su concisión. Sin embargo, sus limitaciones aparecen pronto en otros escenarios.
Si el script necesita estructuras de datos complejas, como diccionarios anidados o listas de objetos, procesar JSON de forma intensiva, manejar errores con detalle o superar unos cientos de líneas, lenguajes como Python ofrecen mejor legibilidad, bibliotecas estándar más ricas y pruebas unitarias más sencillas. Para procesar JSON desde Bash, la herramienta jq cubre muchos casos, pero llega un punto en que el código se vuelve difícil de mantener.
Para infraestructura a gran escala, herramientas declarativas como Ansible, Terraform o los pipelines de CI/CD resuelven problemas de idempotencia y de ejecución en muchos servidores que serían complejos de reproducir con scripts propios. Aun así, Bash suele estar presente dentro de esas herramientas, en los pasos de un pipeline o en los ganchos de un despliegue, por lo que dominarlo sigue siendo útil incluso cuando no es la pieza principal.
Un criterio práctico: si el script cabe en una pantalla y consiste sobre todo en llamar a otros programas, Bash es una excelente elección. Si empiezas a implementar lógica de negocio, parseo complejo o necesitas interfaces de usuario, es momento de cambiar de lenguaje.
Conclusiones
Bash sigue siendo una de las habilidades más rentables para cualquier persona que trabaje con Linux, desde desarrolladores que automatizan su entorno local hasta administradores que mantienen flotas de servidores. Con unos pocos conceptos, como el shebang, los permisos de ejecución, las variables, los argumentos posicionales, las redirecciones, las condiciones y los bucles, es posible convertir procedimientos manuales en herramientas fiables que se ejecutan solas.
La diferencia entre un script improvisado y uno profesional está en los detalles: entrecomillar las variables, validar las entradas, activar set -euo pipefail, limpiar recursos con trap, registrar lo que ocurre y apoyarse en ShellCheck para detectar errores antes de que lleguen a producción. Programar las tareas con cron, o con temporizadores de systemd en sistemas modernos, completa el ciclo de automatización.
Con GNU Bash 5.3 como versión estable actual y una compatibilidad que se extiende a prácticamente todas las distribuciones, lo que aprendas hoy seguirá siendo válido durante años. El siguiente paso natural es tomar una tarea que repites con frecuencia, escribir su primer script siguiendo estas prácticas y ampliarlo poco a poco a medida que surjan nuevas necesidades.
