filecmp en Python: compara archivos y carpetas

Publicado el: 03/08/2026
Tempo de leitura: 6 minutos

Los sincronizadores, backups, pruebas de despliegue y herramientas de auditoría necesitan descubrir si archivos o árboles de directorios son iguales. Leer cada byte siempre funciona, pero puede ser costoso con miles de elementos. El módulo filecmp en Python ofrece comparación eficiente de archivos individuales, lotes y directorios, con opción de verificar metadatos o contenido real.

Esta guía explica cmp(), cmpfiles() y dircmp, incluido el parámetro shallow, la caché basada en stat(), la comparación recursiva, los enlaces simbólicos, la concurrencia y cuándo añadir hashes. Complementa nuestros artículos sobre difflib, archivos temporales, lectura de archivos, PermissionError y collections.

Comparación básica con cmp()

filecmp.cmp() recibe dos rutas e indica si los archivos parecen iguales.

from filecmp import cmp

iguales = cmp("original.txt", "copia.txt")
print(iguales)

El valor predeterminado shallow=True permite considerar iguales los archivos cuya firma de os.stat() coincide. Esa firma incluye tipo, tamaño y fecha de modificación.

Comparación superficial

Una comparación superficial evita leer el contenido cuando los metadatos ya coinciden.

iguales = cmp("a.bin", "b.bin", shallow=True)

Es rápida para cachés, builds y directorios controlados, pero no demuestra que todos los bytes sean iguales. Dos archivos diferentes pueden tener el mismo tamaño y timestamp.

Comparación por contenido

Usa shallow=False para comparar los bytes.

iguales = cmp("a.bin", "b.bin", shallow=False)

La documentación oficial de filecmp indica que la función aún puede reutilizar un resultado en caché. La entrada se invalida cuando cambia la información de stat().

Caché y escrituras rápidas

En sistemas de archivos con poca resolución temporal, un archivo puede reescribirse rápidamente sin cambio visible en la fecha. Si el tamaño también permanece igual, podría reutilizarse un resultado antiguo.

import filecmp

filecmp.clear_cache()
iguales = filecmp.cmp("a.txt", "b.txt", shallow=False)

clear_cache() es útil en pruebas y justo después de escrituras rápidas. Cerrar y ejecutar flush antes de comparar reduce ambigüedades.

Validar archivos regulares

cmp() está diseñada para archivos regulares. Valida las rutas.

from pathlib import Path
from filecmp import cmp


def archivos_iguales(a, b):
    pa = Path(a)
    pb = Path(b)
    if not pa.is_file() or not pb.is_file():
        raise ValueError("Ambas rutas deben ser archivos")
    return cmp(pa, pb, shallow=False)

Los enlaces, dispositivos y archivos especiales necesitan una política. Decide si comparas el enlace, su destino o lo rechazas.

Comparar varios nombres con cmpfiles()

cmpfiles() compara archivos con el mismo nombre relativo en dos directorios.

from filecmp import cmpfiles

nombres = ["app.py", "config.toml", "README.md"]
iguales, diferentes, errores = cmpfiles(
    "version-a",
    "version-b",
    nombres,
    shallow=False,
)

print("Iguales:", iguales)
print("Diferentes:", diferentes)
print("Errores:", errores)

La lista de errores contiene archivos ausentes, inválidos o ilegibles. No trates un error como una diferencia ordinaria; informa la causa.

Archivos ausentes y permisos

La comparación puede fallar por permisos, enlaces rotos o cambios concurrentes.

from pathlib import Path

for nombre in errores:
    ruta_a = Path("version-a") / nombre
    ruta_b = Path("version-b") / nombre
    print(nombre, ruta_a.exists(), ruta_b.exists())

Una auditoría debe distinguir ausente, sin permiso, tipo incompatible y cambiado durante la lectura.

Análisis de directorios con dircmp

La clase dircmp compara dos árboles y expone listas y comparaciones anidadas.

from filecmp import dircmp

comparacion = dircmp("proyecto-a", "proyecto-b")
print(comparacion.left_only)
print(comparacion.right_only)
print(comparacion.common_files)
print(comparacion.diff_files)

Los atributos se calculan bajo demanda. Crear el objeto no recorre necesariamente todo el árbol.

Atributos principales de dircmp

  • left_list y right_list: entradas de cada lado;
  • common: nombres presentes en ambos;
  • left_only y right_only: nombres exclusivos;
  • common_dirs: subdirectorios compartidos;
  • common_files: archivos regulares comunes;
  • common_funny: tipos incompatibles o fallos de stat();
  • same_files, diff_files y funny_files: resultados para archivos comunes.

Comprobar solo diff_files omite archivos exclusivos.

Informes integrados

report() imprime el nivel actual. report_partial_closure() añade subdirectorios inmediatos y report_full_closure() recorre todo.

comparacion.report_full_closure()

Estos métodos imprimen en salida estándar. Una API o prueba debe consumir los atributos y construir datos estructurados.

Recursión mediante subdirs

subdirs asocia cada subdirectorio común con otro objeto dircmp.

def recoger(comp, prefijo=""):
    resultado = []
    for nombre in comp.left_only:
        resultado.append(("solo_izquierda", prefijo + nombre))
    for nombre in comp.right_only:
        resultado.append(("solo_derecha", prefijo + nombre))
    for nombre in comp.diff_files:
        resultado.append(("diferente", prefijo + nombre))
    for nombre, hijo in comp.subdirs.items():
        resultado.extend(recoger(hijo, prefijo + nombre + "/"))
    return resultado

Protege la aplicación contra árboles enormes, profundidad excesiva y modificaciones concurrentes.

shallow en dircmp

Desde Python 3.13, dircmp acepta shallow.

comparacion = dircmp("a", "b", shallow=False)

Usa False cuando same_files y diff_files deben reflejar contenido real.

Ignorar nombres

El parámetro ignore sustituye la lista predeterminada.

comparacion = dircmp(
    "a",
    "b",
    ignore=[".git", "__pycache__", ".venv", "node_modules"],
    shallow=False,
)

Si proporcionas una lista, incluye todo lo que debe continuar ignorado. No ocultes artefactos relevantes solo para simplificar el informe.

El parámetro hide

hide controla nombres omitidos de los informes, con valores por defecto como . y ...

En general, ignore elimina elementos de la comparación, mientras hide afecta la presentación.

Explicar diferencias textuales con difflib

filecmp informa que dos archivos difieren, pero no explica cómo. Genera un diff después.

from difflib import unified_diff
from pathlib import Path

antes = Path("a/config.ini").read_text(encoding="utf-8").splitlines(True)
despues = Path("b/config.ini").read_text(encoding="utf-8").splitlines(True)

print("".join(unified_diff(antes, despues, fromfile="a", tofile="b")))

Para binarios, informa tamaño, hash o utiliza una herramienta del formato.

Cuándo usar hashes

Una comparación byte a byte responde si el contenido actual coincide. Un digest persistido permite verificar integridad después o en otra máquina.

from hashlib import file_digest


def sha256(ruta):
    with open(ruta, "rb") as archivo:
        return file_digest(archivo, "sha256").hexdigest()

La documentación oficial de hashlib ofrece SHA-256 y otros algoritmos. Evita MD5 y SHA-1 para nuevos diseños contra manipulación.

Un hash también lee el archivo

Calcular un digest lee todos los bytes y puede costar más que una comparación que se detiene en la primera diferencia. Usa hashes cuando necesitas guardar, transmitir, firmar o comparar repetidamente una huella.

Condiciones de carrera

Un archivo puede cambiar entre stat(), lectura e informe. El resultado describe un momento aproximado, no un snapshot transaccional.

Los backups críticos deben usar snapshots, bloqueos adecuados o copias temporales. Revisa de nuevo los metadatos si la consistencia es esencial.

Enlaces simbólicos

La resolución depende de las operaciones del sistema. Un sincronizador debe definir si conserva enlaces o compara destinos.

Usa Path.is_symlink() y os.readlink() cuando importa la identidad del enlace. Impide que enlaces seguidos salgan de una raíz autorizada.

Archivos y árboles grandes

cmp(..., shallow=False) lee por bloques y no carga todo en memoria. Aun así, comparar muchos archivos grandes consume I/O.

Usa metadatos como filtro inicial, limita concurrencia y prioriza elementos modificados. Los almacenamientos remotos pueden ofrecer checksums del servidor.

Pruebas con directorios temporales

from pathlib import Path
from tempfile import TemporaryDirectory
from filecmp import dircmp

with TemporaryDirectory() as a, TemporaryDirectory() as b:
    Path(a, "igual.txt").write_text("ok", encoding="utf-8")
    Path(b, "igual.txt").write_text("ok", encoding="utf-8")
    Path(a, "diferente.txt").write_text("A", encoding="utf-8")
    Path(b, "diferente.txt").write_text("B", encoding="utf-8")

    comp = dircmp(a, b, shallow=False)
    assert "igual.txt" in comp.same_files
    assert "diferente.txt" in comp.diff_files

Prueba también elementos exclusivos, carpetas, permisos, enlaces y escrituras rápidas.

Ejemplo de auditoría estructurada

def auditar(a, b):
    comp = dircmp(a, b, shallow=False)
    return {
        "solo_a": comp.left_only,
        "solo_b": comp.right_only,
        "iguales": comp.same_files,
        "diferentes": comp.diff_files,
        "errores": comp.funny_files + comp.common_funny,
        "subdirectorios": {
            nombre: auditar(hijo.left, hijo.right)
            for nombre, hijo in comp.subdirs.items()
        },
    }

En árboles no confiables, impone límites de profundidad y cantidad.

Errores frecuentes

  • Usar shallow=True como prueba criptográfica.
  • Ignorar left_only y right_only.
  • Tratar archivos ilegibles como diferencias normales.
  • Olvidar la caché tras escrituras rápidas.
  • Comparar enlaces sin una política.
  • Calcular hash para todo sin considerar el coste.
  • Suponer que el árbol no cambia durante el análisis.
  • Imprimir informes cuando se necesitan datos estructurados.

Buenas prácticas

  • Elige conscientemente metadatos o contenido.
  • Usa cmpfiles() para lotes conocidos.
  • Usa dircmp para estructura y recursión.
  • Clasifica ausencias, diferencias y errores por separado.
  • Usa difflib para explicar cambios textuales.
  • Usa SHA-256 cuando necesitas una huella persistente.
  • Limita profundidad, cantidad y concurrencia.
  • Prueba cambios rápidos y varias plataformas.

Conclusión

El módulo filecmp en Python ofrece una base sencilla para comparar archivos y directorios. cmp() maneja dos archivos, cmpfiles() clasifica un lote y dircmp revela diferencias estructurales y recursivas.

La fiabilidad depende del nivel elegido. Los metadatos aportan velocidad, el contenido ofrece una confirmación más fuerte y los hashes permiten conservar una huella. Al separar errores de diferencias, considerar la caché, definir políticas para enlaces y manejar concurrencia, puedes crear verificadores de despliegue, backups y auditorías con resultados claros.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Icono de archivo digital que representa tipos MIME con mimetypes en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    mimetypes en Python: tipos MIME

    Aprende mimetypes en Python para identificar tipos MIME, extensiones y encodings de forma segura en cargas, descargas, correo y APIs

    Ler mais

    Tempo de leitura: 6 minutos
    08/08/2026
    Búsqueda binaria y listas ordenadas con bisect en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    bisect en Python: listas ordenadas

    Aprende bisect en Python para búsqueda binaria, inserción ordenada, duplicados, rangos y diseño seguro de listas.

    Ler mais

    Tempo de leitura: 5 minutos
    08/08/2026
    Código y archivos empaquetados con importlib.resources en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    importlib.resources en Python: guía práctica

    Aprende importlib.resources en Python para acceder a archivos empaquetados con seguridad en wheels y aplicaciones instaladas.

    Ler mais

    Tempo de leitura: 5 minutos
    07/08/2026
    Teclado y flujo de datos que representa el procesamiento de varios archivos con fileinput en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    fileinput en Python: lee varios archivos

    Aprende fileinput en Python para leer varios archivos o stdin, rastrear líneas, abrir archivos comprimidos y reescribir contenido con backups.

    Ler mais

    Tempo de leitura: 7 minutos
    07/08/2026
    Editor de código con líneas numeradas que representa el módulo linecache en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    linecache en Python: lee líneas por número

    Aprende linecache en Python para leer líneas por número, administrar la caché, actualizar archivos modificados e integrar traceback y loaders.

    Ler mais

    Tempo de leitura: 7 minutos
    07/08/2026
    Datos binarios que representan serialización interna con marshal en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    marshal en Python: serialización interna

    Aprende marshal en Python para serializar tipos internos, controlar versiones y bloquear objetos de código cuando no sean necesarios.

    Ler mais

    Tempo de leitura: 6 minutos
    06/08/2026