Skip to main content

Command Palette

Search for a command to run...

De Bash a Python: Automatización, IA y las Tres Leyes del SysAdmin

De un 'One-Liner' a un Script de Respaldo

Updated
8 min readView as Markdown
De Bash a Python: Automatización, IA y las Tres Leyes del SysAdmin
A
Geek by nature, Linux by choice, Fedora of course ...

A veces, un día festivo es el pretexto perfecto para darle mantenimiento a nuestros propios sistemas. Recientemente, mi partición /home llegó a ese crítico 99% de uso, y lo que empezó como un simple one-liner en Bash para identificar qué borrar, terminó evolucionando a una herramienta interactiva en Python de más de 400 líneas.

⚠️ IMPORTANTE: No soy un experto en Python; me encuentro en pleno desarrollo de esta habilidad. Este script es el resultado de mi aprendizaje activo, por lo que cualquier sugerencia de mejora, optimización o reporte de errores es más que bienvenida.

Durante el desarrollo de esta herramienta, y al ver cómo se iba transformando, reflexioné bastante sobre la filosofía detrás de nuestro trabajo diario y cómo se está integrando la Inteligencia Artificial en él.

Las Tres Leyes del SysAdmin

Basándome en las inmortales leyes de la robótica de Asimov, he sintetizado lo que considero los pilares de nuestra profesión:

  1. Primera Ley: Un SysAdmin debe respaldar TODO el sistema, y validar el respaldo regularmente.

  2. Segunda Ley: Un SysAdmin debe dominar la línea de comandos, y evitar las interfaces gráficas, excepto si entra en conflicto con la Primera Ley.

  3. Tercera Ley: Un SysAdmin debe automatizar al máximo para tener tiempo libre productivo, hasta donde este tiempo libre no entre en conflicto con la Primera o la Segunda Ley.

Para los puristas, esto se resume en latín como: Custodi, Automatiza, Stude (Respalda, Automatiza, Estudia).

El rol de la IA: Copiloto, no Piloto Automático

En mi día a día desarrollando material técnico, el uso de herramientas de IA corporativas se ha vuelto un estándar. Empecé siendo escéptico, pero con el tiempo las bondades y los límites se esclarecieron.

No estoy en contra del uso de IA en el desarrollo; discrepo en hasta qué punto nos permitimos automatizar. Veo una tendencia a automatizarlo todo ciegamente: crear la lectura, generar el ejercicio, escribir el script y probarlo, todo con un solo prompt. Esto deja de lado que el material es para humanos que quieren aprender, y la revisión nunca debe delegarse por completo a la máquina.

Por eso, he encontrado un equilibrio: utilizo la IA para revisiones de estilo y docencia, pero para el desarrollo, prefiero un enfoque de pair-programming. Me involucro, hago las pruebas, analizo los errores y exijo que la IA me explique el porqué y el cómo detrás de la solución. Que sea un asistente poderoso, pero que nos permita aprender.

El desarrollo de smart_backup.py es el ejemplo perfecto. Empezó como un ls -ltr | grep ^d | awk '{ print $9 }' | xargs du -sk 2> /dev/null... en Bash, y a través de iteraciones basadas en las necesidades reales del sistema, construimos una solución modular y robusta.

A continuación, desglosaré cómo está construida esta herramienta, función por función, y el razonamiento técnico detrás de cada decisión.

Para que el script funcione en tu equipo, solo asegúrate de tener instalados Python 3, rsync y tar.

💡 Nota sobre el código: Por el ajuste de línea automático de este blog, algunas líneas largas de Python en los siguientes bloques pueden aparecer divididas, lo que rompe la estricta indentación del lenguaje. Para evitar errores de sintaxis (IndentationError) al copiar y pegar, te recomiendo encarecidamente revisar y descargar el script original directamente desde el repositorio en GitHub.

1. El Motor de Búsqueda y Utilidades

Cualquier respaldo eficiente necesita saber qué vale la pena mover. Nuestro primer problema era el "ruido": directorios como .obsidian pueden tener miles de archivos diminutos que abruman cualquier salida de terminal.

import os
import subprocess
import sys
import datetime
import shutil

SIZE_THRESHOLD = 5 * 1024 * 1024 * 1024 # 5GB
HOME = os.path.expanduser("~")
USER = os.environ.get("USER", "usuario")

def get_size(path):
    if os.path.isfile(path):
        return os.path.getsize(path)
    total_size = 0
    for dirpath, _, filenames in os.walk(path):
        for f in filenames:
            fp = os.path.join(dirpath, f)
            if not os.path.islink(fp):
                total_size += os.path.getsize(fp)
    return total_size

¿Por qué lo hicimos así?
Definimos un SIZE_THRESHOLD de 5GB. En lugar de iterar a ciegas, la función get_size() calcula el peso recursivo de cada ruta, ignorando enlaces simbólicos para no caer en bucles infinitos. Esto nos permite filtrar únicamente los verdaderos "devoradores de espacio".

2. Organización y Versionado

Si ejecutas el respaldo varias veces al día, no quieres que se sobreescriban tus datos.

def get_archive_dir(base_dest, prefix):
    meses = {1:"Ene", 2:"Feb", 3:"Mar", 4:"Abr", 5:"May", 6:"Jun", 
             7:"Jul", 8:"Ago", 9:"Sept", 10:"Oct", 11:"Nov", 12:"Dic"}
    now = datetime.datetime.now()
    date_str = f"{now.day}{meses[now.month]}{now.year}"
    
    base_name = f"{prefix}_{date_str}"
    target_dir = os.path.join(base_dest, base_name)
    
    if not os.path.exists(target_dir):
        return target_dir
    
    counter = 1
    while True:
        target_dir = os.path.join(base_dest, f"{base_name}_{counter}")
        if not os.path.exists(target_dir):
            return target_dir
        counter += 1

¿Por qué lo hicimos así?
Esta función genera rutas dinámicas como SystemBackup_17Sept2026. Si el directorio ya existe, anexa un contador (_1, _2). Mantiene el disco externo impecable y auditable.

3. El Respaldo de Sistema (La Lista de Oro)

Restaurar un perfil de Linux en una reinstalación debe ser indoloro. Aquí es donde atacamos las configuraciones (.config, .ssh, .bashrc).

def backup_system(dest_path):
    # ... (código de inicialización) ...
    sys_items = [
        ".bashrc", ".bash_profile", ".bash_history", ".zshrc", 
        ".ssh", "bin", ".gitconfig", ".git-credentials", 
        ".vim", ".vimrc", ".viminfo", ".ansible", ".aws", 
        ".ollama", ".claude", ".config", ".local"
    ]
    
    to_pack = [item for item in sys_items if os.path.exists(os.path.join(HOME, item))]
    
    # ... (lógica interactiva para agregar/excluir) ...
    
    total_sys_size = sum(item_sizes[item] for item in to_pack)
    free_space = shutil.disk_usage(os.path.dirname(dest_path)).free
    
    if total_sys_size > free_space:
        print("❌ ERROR CRÍTICO: No hay suficiente espacio en el destino.")
        return

    cmd = ["tar", "-czf", tar_filepath] + to_pack
    subprocess.run(cmd, cwd=HOME, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)

¿Por qué lo hicimos así?

  • Empaquetado en Tarball: Usamos tar en lugar de una copia cruda. Esto es vital porque si tu disco externo está en exFAT o NTFS, perderías todos los permisos de Linux, grupos y enlaces simbólicos. El .tar.gz encapsula y protege todo eso.

  • Exclusión interactiva: Descubrimos durante las pruebas que directorios como .ollama o .local pueden pesar más de 10GB. Tratar de comprimir eso a un solo hilo con gzip satura el CPU y toma una eternidad. Añadimos un menú interactivo para excluir a los "pesos pesados" si tenemos prisa.

  • Seguro de espacio (shutil): Antes de iniciar, comparamos el tamaño sin comprimir contra el espacio libre del disco. Prevenimos archivos corruptos por falta de espacio a medio proceso.

4. El Respaldo Personal (Liberando Espacio)

El objetivo aquí no era solo copiar fotos o ISOs, sino sacarlas del disco local para revivir ese /home al 99%.

def backup_personal(dest_path, rsync_flags):
    # ... (menús de depuración interactiva) ...
    
    if confirm == 's':
        os.makedirs(dest_path, exist_ok=True)
        log_file_path = os.path.join(dest_path, "backup_registro.log")
        list_file = "/tmp/rsync_items.list"
        
        with open(list_file, 'w') as f:
            for item in to_sync:
                rel_path = os.path.relpath(item, HOME)
                f.write(f"{rel_path}\n")

        cmd = [
            "rsync", rsync_flags,
            f"--log-file={log_file_path}",
            f"--files-from={list_file}",
            "--remove-source-files",
            f"{HOME}/", f"{dest_path}/"
        ]
        subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)

¿Por qué lo hicimos así?

  • Drill-Down Inteligente: En lugar de copiar todo Pictures, el script te permite "Depurar". Entra al directorio, evalúa los subniveles y te pregunta qué respaldar y qué ignorar.

  • Transaccionalidad con Rsync: Usamos la bandera --remove-source-files. Esta es la magia negra que elimina el archivo original de la laptop únicamente si la copia al destino fue 100% exitosa.

  • Logs Limpios: rsync puede generar una salida en pantalla ilegible si copias miles de archivos. Redirigimos todo a un archivo backup_registro.log dentro de la carpeta de respaldo y silenciamos la consola mediante stdout=subprocess.DEVNULL.

5. Detección Inteligente del FileSystem

Un detalle crítico de la automatización es evitar que el script falle por incompatibilidades de hardware o formato.

def main():
    # ... (validaciones de ruta) ...
    try:
        fs_type = subprocess.check_output(f"df -T '{dest_dir}' | awk 'NR==2 {{print $2}}'", shell=True, text=True).strip()
    except Exception:
        fs_type = "unknown"

    rsync_flags = "-avh"
    if fs_type in ["exfat", "vfat", "fuseblk", "ntfs"]:
        print(f"ℹ️  Sistema de archivos '{fs_type}' detectado. Usando banderas de compatibilidad.")
        rsync_flags = "-rtvh"

¿Por qué lo hicimos así?
Si formateas tu disco en exFAT para que tus hijos puedan leerlo en Windows, la bandera nativa -a (archive) de rsync fallará estrepitosamente al intentar replicar dueños y permisos de Linux. El script intercepta esto; si detecta exFAT o NTFS, cambia dinámicamente las banderas a -rtvh (recursivo y preservando tiempos de modificación), garantizando una copia cruzada sin errores de permisos.

Conclusión

Automatizar no es ejecutar comandos a ciegas. Es plasmar la lógica, la precaución y la experiencia operativa dentro de una herramienta que te cubra las espaldas. A través de la interacción iterativa con IA, pasé de una tarea tediosa manual a un binario robusto que valida espacio, interactúa con el usuario, detecta sistemas de archivos y, lo más importante, me deja tiempo libre productivo.

¿Estás de acuerdo con el uso de la IA para aprender a programar? Me encantaría leer tus opiniones.

🔗 ¡Descarga, usa o haz un fork del código completo en GitHub! https://github.com/rootzilopochtli/smart-backup

Espero les sirva.