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

    Terminal de comandos que representa parsing seguro con shlex en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    shlex en Python: comandos seguros

    Aprende shlex en Python para separar comandos, tratar comillas, usar quote y join y reducir riesgos de inyección de shell.

    Ler mais

    Tempo de leitura: 6 minutos
    02/08/2026
    Base de datos local que representa persistencia con shelve en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    shelve en Python: persistencia simple

    Aprende shelve en Python para persistir objetos, actualizar datos mutables, evitar riesgos de pickle y decidir cuándo migrar a SQLite.

    Ler mais

    Tempo de leitura: 5 minutos
    01/08/2026
    Documentos de texto que representan comparación de versiones con difflib en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    difflib en Python: compara textos y archivos

    Aprende difflib en Python para comparar textos, medir similitud, crear diffs unificados, informes HTML y sugerencias de nombres.

    Ler mais

    Tempo de leitura: 6 minutos
    01/08/2026
    Panel de gráficos que representa análisis estadístico de datos en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    statistics en Python: análisis de datos

    Aprende statistics en Python para media, mediana, desviación, cuantiles, correlación, regresión, NormalDist y KDE.

    Ler mais

    Tempo de leitura: 7 minutos
    31/07/2026
    Gráficos de fracciones que representan números racionales exactos en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    fractions en Python: números racionales

    Aprende fractions en Python para aritmética racional exacta, reducción automática, limit_denominator, formato y conversiones seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    31/07/2026
    Calculadora y documentos que representan cálculos Decimal precisos en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    Decimal en Python: cálculos precisos

    Aprende Decimal en Python para cálculos exactos, dinero, quantize, redondeo, contextos y validación sin errores de float.

    Ler mais

    Tempo de leitura: 7 minutos
    30/07/2026