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:
- Ahorro de tiempo: Lo que te tomaba 20 minutos ahora toma segundos
- Consistencia: El script siempre hace lo mismo, sin variaciones
- 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:
#!/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:
- Docstrings que expliquen qué hace cada función
- Manejo de errores con try/except donde sea apropiado
- Salida clara por stdout (lo que el usuario ve) y stderr (errores)
- Código en la parte inferior protegido con
if __name__ == "__main__":
Guarda el archivo y hazlo ejecutable:
chmod +x espacio.pyConfigurando cron para ejecución automática
Cron usa un formato de cinco campos para decidir cuándo ejecutar un 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)
│ │ │ │ │
* * * * * comando_a_ejecutarEjemplos 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:
crontab -eSe abrirá tu editor predeterminado. Agrega tu línea al final y guarda. Para ver tus crontabs actuales:
crontab -lImportante: 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:
#!/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
# Probar primero
python3 /home/nico/scripts/limpieza-logs.py
# Programarlo en cron (diario a las 3 AM)
# 0 3 * * * /home/nico/scripts/limpieza-logs.pyTu 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:
#!/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
chmod +x bateria.py
python3 /home/nico/scripts/bateria.pyDeberías ver algo como:
Batería: 78% - CargandoPrograma 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:
crontab -eY agrega al final:
0 9 * * * /home/nico/scripts/bateria.py >> /home/nico/.local/share/bateria-log.txt 2>&1Desglose 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 log2>&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:
# Forzar ejecución inmediata
crontab -l | grep bateria
# O ejecuta directamente:
~/scripts/bateria.pyDeberí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.
0 9 * * * /home/nico/scripts/bateria.py >> /home/nico/.local/share/bateria-log.txt 2>&13. 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 #:
# 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>&15. 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:
0 9 * * * PATH="/usr/local/bin:/usr/bin:/bin" /home/nico/scripts/miscript.pySolución de problemas comunes
Mi script no se ejecuta
- Verifica que el crontab esté bien formado:
crontab -l - Comprueba que el script sea ejecutable:
ls -l /ruta/espacio.py - Ejecuta el script manualmente:
/ruta/espacio.py - 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:
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.
0 9 * * * /home/nico/scripts/bateria.py >> /home/nico/.local/share/bateria-log.txt 2>&1Conclusió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.