Saltar al contenido
src / articulos / 09-archivos.md

Operaciones de I/O: Manejo de Archivos en Python

Emilio Castro //
// NARRACIÓN 0:00 / 0:00

La mayoría de los servidores se configuran y diagnostican a través de archivos de texto plano: .conf, .yaml, /var/log/*. Saber abrirlos, analizarlos y editarlos desde código es lo que separa el trabajo manual del que escala solo.

Python maneja I/O a través de descriptores de archivo, con mecanismos concretos para no agotar los límites del sistema cuando procesas miles de líneas de logs.

La Función open() y sus Modos

En Python se usa la función integrada open(ruta, modo) para acceder al sistema de archivos del SO. El “modo” determina los permisos y el comportamiento del puntero interno.

  • 'r' (Lectura): El valor por defecto. Falla si el archivo no existe.
  • 'w' (Escritura destructiva): Sobrescribe todo el archivo si existe, o crea uno nuevo en blanco. Úsalo con mucho cuidado: es muy fácil borrar el /etc/nginx/nginx.conf por accidente.
  • 'a' (Append / Añadir): Mueve el puntero al final del archivo y agrega contenido sin tocar el histórico. Es el modo que usan los rotadores de logs y cualquier script de registro continuo.
  • 'b' (Binario): Lee o escribe en secuencias de bytes puros. Solo hace falta para descargar imágenes o manipular archivos comprimidos (tar, gzip).

Context Managers: El Bloque with

Cuando el SO abre un archivo, reserva recursos en memoria. Si un script de monitorización termina abruptamente sin cerrar el archivo (.close()), el descriptor de archivo (FD) queda atrapado. Agotar los FD en Linux, vía ulimit, es más fácil de lo que parece: el servidor deja de aceptar conexiones de red y no puede lanzar nuevos procesos.

El patrón antiguo de abrir y acordarse de cerrar no funciona en producción. Un Context Manager (with) cierra y libera el archivo en cuanto el bloque termina, aunque explote una excepción dentro. Punto.

# Patrón Seguro (Recomendado)
with open('/var/log/syslog', 'r') as log_file:
    # Procesar las primeras 100 líneas del log
    lineas = log_file.readlines()
    print(lineas[0]) # Imprime la primera entrada del syslog
# El archivo ya está cerrado para este momento (fuera del bloque).

Análisis Eficiente de Archivos Gigantes (Lectura)

Cargar un log de 5 GB con .read() mata el servidor por OOM Kill. Todos esos gigabytes van directo a la RAM del proceso. No hay forma elegante de recuperarse de eso en producción.

La forma idiomática es iterar el archivo línea por línea. El consumo de RAM es casi nulo sin importar si el archivo pesa 10 MB o 50 GB.

# Escaneando 5GB de logs de NGINX buscando un Error 502
try:
    with open('/var/log/nginx/access.log', 'r') as access_log:
        for linea in access_log:
            # Procesamos cada línea independientemente y la descartamos
            if " 502 " in linea:
                print(f"Error detectado: {linea.strip()}")
except FileNotFoundError:
    print("[WARN] El log no existe. Verificar si el demonio Nginx está instalado.")

Escritura de Estados o Alertas ('w' y 'a')

Imagina que automatizaste un script para generar listas de bloqueos perimetrales de IP. Si necesitas exportar esto a un archivo, usaremos el modo destructivo ('w') para reescribir un archivo completo, y el aditivo ('a') para registrar auditoría.

ips_atacantes = ["192.168.1.100", "10.0.4.45"]

# Generar un archivo nuevo con el listado fresco
with open('/etc/firewall/ban_list.txt', 'w') as ban_file:
    for ip in ips_atacantes:
        # Añadimos manualmente el salto de línea (\n)
        ban_file.write(f"deny {ip};\n")

# Registrar el evento en el log de auditoría (sin borrar el histórico)
from datetime import datetime
with open('/var/log/security_audit.log', 'a') as audit_log:
    timestamp = datetime.now().isoformat()
    audit_log.write(f"[{timestamp}] Reglas de firewall actualizadas exitosamente.\n")

Con with y lectura por líneas tienes el 90% de lo que necesitas para trabajar con archivos en infraestructura real. Lo que falta es contexto del dominio: saber qué buscar en un syslog, o cuándo conviene aplastar un archivo de configuración en vez de añadir al final.