Introducción

Llegó el momento de dejar de hacer las mismas tareas manuales una y otra vez. En este artículo verás cómo usar Python para crear scripts automatizados y cron, el programador de tareas más fiable de los sistemas Unix, para ejecutarlos automáticamente. Al final, tendrás herramientas concretas para ahorrar tiempo en tu día a día.

¿Por qué automatizar?

Automatizar tareas repetitivas ofrece tres beneficios principales:

  1. Ahorro de tiempo: Lo que te tomaba 20 minutos ahora toma segundos
  2. Consistencia: El script siempre hace lo mismo, sin variaciones
  3. Reducción de errores humanos: Olvidaste ejecutar el backup? El script no

Para un usuario como Nico, que gestiona servidores y trabaja con desarrollo, la automatización se vuelve esencial. En lugar de estar recordando ejecutar copias de seguridad, limpiezas o envíos de reportes, delegas eso en scripts que corren solos.

Requisitos previos

Antes de empezar, asegúrate de tener:

  • Sistema Linux (o WSL2 en Windows con acceso a crontab)
  • Python 3.8+ instalado (python3 --version)
  • Permisos para editar el crontab del usuario (crontab -e)
  • Un editor de texto cómodo (nano, micro, o tu favorito)

Creando tu primer script Python

Vamos a crear un script simple que verifica el espacio en disco y envía un reporte. Crea un archivo llamado espacio.py:

ice-tech
#!/usr/bin/env python3
import os
import sys
from datetime import datetime

def check_disk_usage():
    """Verifica el uso de disco y devuelve un reporte formateado."""
    stat = os.statvfs('/')
    total_gb = (stat.f_frsize * stat.f_blocks) / (1024**3)
    free_gb = (stat.f_frsize * stat.f_bavail) / (1024**3)
    used_percent = ((total_gb - free_gb) / total_gb) * 100

    now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    message = f"[{now}] Espacio en disco: {used_percent:.1f}% usado ({free_gb:.2f} GB libres de {total_gb:.2f} GB)"
    print(message)
    return used_percent

if __name__ == "__main__":
    percent = check_disk_usage()
    # Umbral de alerta: si hay menos del 10% de espacio libre, avisar
    if percent > 90:
        print("⚠️ ADVERTENCIA: Pocos espacio disponible en disco")
        sys.exit(1)
    sys.exit(0)

Estructura del script

Todo script Python útil debería tener:

  1. Docstrings que expliquen qué hace cada función
  2. Manejo de errores con try/except donde sea apropiado
  3. Salida clara por stdout (lo que el usuario ve) y stderr (errores)
  4. Código en la parte inferior protegido con if __name__ == "__main__":

Guarda el archivo y hazlo ejecutable:

ice-tech
chmod +x espacio.py

Configurando cron para ejecución automática

Cron usa un formato de cinco campos para decidir cuándo ejecutar un comando:

ice-tech
┌───────── minuto (0 - 59)
│ ┌──────── hora (0 - 23)
│ │ ┌──────── día del mes (1 - 31)
│ │ │ ┌──── mes (1 - 12)
│ │ │ │ ┌──── día de la semana (0 - 6) (Domingo=0)
│ │ │ │ │
* * * * * comando_a_ejecutar

Ejemplos prácticos

Frecuencia Línea crontab Significado
Cada minuto * * * * * /ruta/espacio.py Cada minuto
Diario a las 2 AM 0 2 * * * /ruta/espacio.py Todos los días a las 02:00
Semanal los lunes 0 3 * * 1 /ruta/espacio.py Cada lunes a las 03:00
Primer día del mes 0 4 1 * * /ruta/espacio.py Del 1 de cada mes a las 04:00

Editando tu crontab

Para editar tus tareas programadas:

ice-tech
crontab -e

Se abrirá tu editor predeterminado. Agrega tu línea al final y guarda. Para ver tus crontabs actuales:

ice-tech
crontab -l

Importante: rutas absolutas

Siempre usa rutas absolutas en tus scripts de cron. Lo que funciona en tu terminal (./archivo.py) puede fallar en cron porque el directorio de trabajo no es el mismo. Usa /home/nico/scripts/espacio.py en lugar de ./espacio.py.

Ejemplo práctico: Script de limpieza de logs

Vamos a crear un script más complejo que rota y comprime logs antiguos. Este es un caso de uso muy común en servidores.

Crea /home/nico/scripts/limpieza-logs.py:

ice-tech
#!/usr/bin/env python3
"""
Limpieza automática de logs antiguos.

Elimina logs con más de 30 días y comprime los de entre 7 y 30 días.
"""

import os
import sys
import glob
import gzip
import shutil
import time
from datetime import datetime, timedelta

LOG_DIR = "/var/log/app"
DAYS_TO_KEEP = 30
DAYS_TO_COMPRESS = 7

def get_file_age_days(filepath):
    """Calcula la edad de un archivo en días."""
    mtime = os.path.getmtime(filepath)
    now = time.time()
    return (now - mtime) / 86400

def compress_log(filepath):
    """Comprime un archivo usando gzip."""
    try:
        with open(filepath, 'rb') as f_in:
            with gzip.open(filepath + '.gz', 'wb') as f_out:
                shutil.copyfileobj(f_in, f_out)
        os.remove(filepath)
        return True
    except Exception as e:
        print(f"Error comprimiendo {filepath}: {e}", file=sys.stderr)
        return False

def main():
    hoy = datetime.now()

    if not os.path.isdir(LOG_DIR):
        print(f"Directorio {LOG_DIR} no existe", file=sys.stderr)
        sys.exit(1)

    # Encontrar todos los archivos de log
    logs = glob.glob(os.path.join(LOG_DIR, "*.log"))

    for log_path in logs:
        dias = get_file_age_days(log_path)

        if dias > DAYS_TO_COMPRESS:
            # Eliminar logs viejos (> 30 días)
            try:
                os.remove(log_path)
                now_str = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                print(f"[{now_str}] Eliminado: {log_path} ({dias:.1f} días old)")
            except Exception as e:
                print(f"Error eliminando {log_path}: {e}", file=sys.stderr)
        elif dias > DAYS_TO_KEEP:
            # Este rango no debería ocurrir, pero por si acaso
            pass
        elif dias > 0:
            # Comprimir logs entre 7 y 30 días
            compress_log(log_path)
            now_str = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            print(f"[{now_str}] Comprimido: {log_path}")

if __name__ == "__main__":
    main()

Ejecutando el script

ice-tech
# Probar primero
python3 /home/nico/scripts/limpieza-logs.py

# Programarlo en cron (diario a las 3 AM)
# 0 3 * * * /home/nico/scripts/limpieza-logs.py

Tu script de ejemplo: Monitoreo de batería

Para este artículo, voy a crear un script que verifica el estado de la batería y guarda un historial. Este es un tutorial práctico que puedes adaptar inmediatamente.

Crea /home/nico/scripts/bateria.py:

ice-tech
#!/usr/bin/env python3
"""
Monitoreo de batería con historial.

Verifica el estado de la batería cada vez que se ejecuta
y guarda un registro en un archivo CSV para análisis posterior.
"""

import os
import sys
import csv
from datetime import datetime

def get_battery_info():
    """Obtiene información de batería si está disponible."""
    try:
        with open('/sys/class/power_supply/BAT0/capacity', 'r') as f:
            capacity = int(f.read().strip())
        with open('/sys/class/power_supply/BAT0/status', 'r') as f:
            status = f.read().strip()
        return capacity, status
    except (FileNotFoundError, ValueError):
        return None, None

def append_to_csv(filename, row):
    """Añade una fila a un archivo CSV, creando el encabezado si es necesario."""
    file_exists = os.path.isfile(filename)
    with open(filename, 'a', newline='') as f:
        writer = csv.writer(f)
        if not file_exists:
            writer.writerow(['fecha', 'hora', 'capacidad', 'estado'])
        writer.writerow(row)

def main():
    capacidad, status = get_battery_info()

    if capacidad is None:
        print("No se pudo leer información de batería")
        sys.exit(1)

    ahora = datetime.now()
    fecha_str = ahora.strftime('%Y-%m-%d')
    hora_str = ahora.strftime('%H:%M:%S')

    # Guardar en historial
    csv_file = "/home/nico/.local/share/bateria-historial.csv"
    os.makedirs(os.path.dirname(csv_file), exist_ok=True)
    append_to_csv(csv_file, [fecha_str, hora_str, capacidad, status])

    # Mostrar estado actual
    estados = {
        'Charging': 'Cargando',
    'Discharging': 'Descargando',
    'Full': 'Cargado',
    'Not charging': 'No cargando',
    'Unknown': 'Desconocido'
    }
    estado_legible = estados.get(status, status)

    mensaje = f"Batería: {capacidad}% - {estado_legible}"
    print(mensaje)

    # Alertas simples
    if capacidad <= 20 and status == 'Discharging':
        print(f"⚠️ Baja batería! Conecta el cargador pronto.")
    if capacidad >= 80 and status == 'Charging':
        print(f"✅ Batería casi completa ({capacidad}%).")

if __name__ == "__main__":
    main()

Ejecutando el script de batería

ice-tech
chmod +x bateria.py
python3 /home/nico/scripts/bateria.py

Deberías ver algo como:

ice-tech
Batería: 78% - Cargando

Programa tu primer task con cron

Ahora que tienes tu script, vamos a programarlo. Supongamos que quieres revisar la batería cada día a las 9:00 (mañana, cuando empiezas a trabajar) y guardar el historial.

Abre tu crontab:

ice-tech
crontab -e

Y agrega al final:

ice-tech
0 9 * * * /home/nico/scripts/bateria.py >> /home/nico/.local/share/bateria-log.txt 2>&1

Desglose de esta línea:

  • 0 9 * * *: Ejecutar a las 9:00 de cada día
  • /home/nico/scripts/bateria.py: La ruta absoluta a tu script
  • >> /home/nico/.local/share/bateria-log.txt: Añade la salida estándar al archivo de log
  • 2>&1: Redirige los errores también al mismo archivo

Verificando que cron funciona

Después de guardar el crontab, espera a las 9:00 o prueba inmediatamente:

ice-tech
# Forzar ejecución inmediata
crontab -l | grep bateria
# O ejecuta directamente:
~/scripts/bateria.py

Deberías ver el mensaje de batería en tu terminal y el historial se actualizará en el archivo CSV.

Mejores prácticas para cron

1. Siempre usa rutas absolutas

Nunca confíes en el directorio de trabajo de cron. Usa rutas absolutas para todo: el script Python, los archivos de configuración, los destinos de salida.

2. Redirige la salida

Siempre redirige stdout y stderr a un archivo de log. Si cron no tiene salida, los errores se pierden.

ice-tech
0 9 * * * /home/nico/scripts/bateria.py >> /home/nico/.local/share/bateria-log.txt 2>&1

3. Prueba antes de desplegar

Ejecuta tu script manualmente varias veces antes de añadirlo a cron. Verifica que:

  • Los permisos son correctos (chmod +x)
  • Las rutas existen
  • Las alertas funcionan como esperas

4. Comenta tus crontabs

Cuando edites crontab -e, añade comentarios con el signo #:

ice-tech
# Monitoreo de batería diario a las 9 AM
0 9 * * * /home/nico/scripts/bateria.py >> /home/nico/.local/share/bateria-log.txt 2>&1

# Limpieza de logs semanal los domingos a las 3 AM
0 3 * * 0 /home/nico/scripts/limpieza-logs.py >> /var/log/limpieza.log 2>&1

5. Manejo de permisos

Los scripts ejecutados por cron corren con los permisos del usuario dueño del crontab. Si necesitas permisos de root, usa sudo crontab -e o configura sudoers apropiados.

6. Entorno de variables

Cron no carga tu perfil de shell (~/.bashrc, ~/.profile). Si tu script usa variables de entorno, Défínelas dentro del script o en el propio crontab:

ice-tech
0 9 * * * PATH="/usr/local/bin:/usr/bin:/bin" /home/nico/scripts/miscript.py

Solución de problemas comunes

Mi script no se ejecuta

  1. Verifica que el crontab esté bien formado: crontab -l
  2. Comprueba que el script sea ejecutable: ls -l /ruta/espacio.py
  3. Ejecuta el script manualmente: /ruta/espacio.py
  4. Revisa el archivo de log: cat /ruta/log.txt

Python no encuentra módulos

Cron tiene un entorno mínimo. Añade la ruta al principio o usa sys.path:

ice-tech
import sys
sys.path.insert(0, '/usr/lib/python3/dist-packages')

Muchos correos electrónicos de cron

Si tienes MAILTO definido en tu crontab o el sistema está configurado para enviar correos, cada ejecución te enviará un correo. Solución: redirige siempre a un archivo de log.

ice-tech
0 9 * * * /home/nico/scripts/bateria.py >> /home/nico/.local/share/bateria-log.txt 2>&1

Conclusión

Ahora tienes las herramientas para automatizar tareas repetitivas en tu sistema Linux. Con Python para la lógica y cron para la programación, puedes delegar lo rutinario y concentrarte en lo que realmente importa.

En este artículo vimos:

  • Cómo crear scripts Python útiles
  • La sintaxis básica de crontab
  • Ejemplos prácticos (espacio en disco, limpieza de logs, monitoreo de batería)
  • Mejores prácticas para que tus automatizaciones sean fiables

Recuerda la regla de oro: siempre usa rutas absolutas y redirige la salida a archivos de log. Con estos hábitos, tus cron jobs serán una herramienta poderosa en tu día a día.


Resumen rápido

Concepto Comando/Acción
Crear script nano /home/nico/scripts/miscript.py
Hacer ejecutable chmod +x /home/nico/scripts/miscript.py
Editar crontab crontab -e
Ver crontabs crontab -l
Formato cron minuto hora día-mes mes día-semana
Ejemplo diario 9AM 0 9 * * * /ruta/script.py

¿Te gustaría más tutoriales sobre automatización? Déjalelo en los comentarios o visite icenico.es para más contenido técnico.