filecmp no Python: compare arquivos e pastas

Publicado em: 03/08/2026
Tempo de leitura: 7 minutos

Sincronizadores, backups, testes de implantação e ferramentas de auditoria precisam descobrir se arquivos ou árvores de diretórios são iguais. Ler cada byte sempre funciona, mas pode ser caro quando há milhares de itens. O módulo filecmp no Python oferece comparações rápidas entre arquivos, lotes e diretórios, com opção de verificar metadados ou conteúdo real.

Neste guia, você aprenderá a usar cmp(), cmpfiles() e dircmp, entenderá o parâmetro shallow, o cache baseado em stat(), a comparação recursiva e quando complementar o resultado com hashes. O conteúdo se conecta aos artigos sobre difflib, tempfile, leitura de arquivos, PermissionError e collections.

Comparação básica com cmp()

filecmp.cmp() recebe dois caminhos e informa se os arquivos parecem iguais.

from filecmp import cmp

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

O valor padrão shallow=True permite considerar iguais arquivos cuja assinatura de os.stat() coincide. Essa assinatura inclui tipo, tamanho e tempo de modificação.

Comparação superficial

A comparação superficial evita abrir o conteúdo quando os metadados já coincidem.

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

É rápida para caches, builds e diretórios controlados, mas não prova que todos os bytes são iguais. Dois arquivos diferentes podem ter o mesmo tamanho e timestamp.

Comparação por conteúdo

Para ler e comparar os bytes, use shallow=False.

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

A documentação oficial de filecmp informa que, mesmo quando shallow=False, a função pode reutilizar um resultado armazenado em cache. O cache é invalidado quando as informações de stat() mudam.

O cache e timestamps rápidos

Em sistemas com baixa resolução de timestamp, um arquivo pode ser alterado rapidamente sem que o tempo de modificação aparente mude. Se tamanho e timestamp também permanecerem iguais, um resultado antigo pode ser reutilizado.

import filecmp

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

clear_cache() é útil em testes ou comparações realizadas imediatamente depois de uma gravação. Em fluxos normais, fechar e sincronizar os arquivos antes da comparação reduz ambiguidades.

Verificando que os caminhos são arquivos

cmp() foi projetada para arquivos regulares. Valide entradas antes de comparar.

from pathlib import Path
from filecmp import cmp


def arquivos_iguais(a, b):
    pa = Path(a)
    pb = Path(b)
    if not pa.is_file() or not pb.is_file():
        raise ValueError("Os dois caminhos devem ser arquivos")
    return cmp(pa, pb, shallow=False)

Links simbólicos, dispositivos e arquivos especiais exigem uma política própria. Decida se deseja comparar o link, o alvo ou rejeitar o item.

Comparando vários nomes com cmpfiles()

cmpfiles() compara arquivos com nomes relativos iguais em dois diretórios.

from filecmp import cmpfiles

nomes = ["app.py", "config.toml", "README.md"]
iguais, diferentes, erros = cmpfiles(
    "versao-a",
    "versao-b",
    nomes,
    shallow=False,
)

print("Iguais:", iguais)
print("Diferentes:", diferentes)
print("Erros:", erros)

A terceira lista contém arquivos ausentes, inválidos ou que não puderam ser lidos. Não trate erro como simples diferença; registre e investigue a causa.

Tratando PermissionError e arquivos ausentes

Comparações podem falhar por permissões, links quebrados ou concorrência.

from pathlib import Path

for nome in erros:
    caminho_a = Path("versao-a") / nome
    caminho_b = Path("versao-b") / nome
    print(nome, caminho_a.exists(), caminho_b.exists())

Uma aplicação de auditoria deve diferenciar ausente, sem permissão, tipo incompatível e alteração durante a leitura.

Analisando diretórios com dircmp

A classe dircmp compara duas árvores e expõe listas e subcomparações.

from filecmp import dircmp

comparacao = dircmp("projeto-a", "projeto-b")
print(comparacao.left_only)
print(comparacao.right_only)
print(comparacao.common_files)
print(comparacao.diff_files)

Os atributos são calculados sob demanda, então criar o objeto não percorre necessariamente toda a árvore.

Principais atributos de dircmp

  • left_list e right_list: entradas de cada lado;
  • common: nomes presentes em ambos;
  • left_only e right_only: nomes exclusivos;
  • common_dirs: subdiretórios em comum;
  • common_files: arquivos regulares em comum;
  • common_funny: itens com tipos incompatíveis ou erro de stat();
  • same_files, diff_files e funny_files: resultados da comparação de arquivos comuns.

Use todas as categorias relevantes; verificar apenas diff_files pode ignorar arquivos exclusivos.

Relatórios prontos

report() mostra apenas o nível atual. report_partial_closure() inclui subdiretórios comuns imediatos. report_full_closure() percorre recursivamente.

comparacao.report_full_closure()

Esses métodos imprimem em saída padrão. Para aplicações web, JSON ou testes, leia os atributos e construa uma estrutura própria.

Recursão com subdirs

subdirs mapeia cada subdiretório comum para outro objeto dircmp.

def coletar(comp, prefixo=""):
    resultado = []
    for nome in comp.left_only:
        resultado.append(("somente_esquerda", prefixo + nome))
    for nome in comp.right_only:
        resultado.append(("somente_direita", prefixo + nome))
    for nome in comp.diff_files:
        resultado.append(("diferente", prefixo + nome))
    for nome, sub in comp.subdirs.items():
        resultado.extend(coletar(sub, prefixo + nome + "/"))
    return resultado

Proteja a aplicação contra árvores enormes, profundidade excessiva e alterações concorrentes.

shallow em dircmp

Desde Python 3.13, dircmp aceita o argumento shallow.

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

Use False quando same_files e diff_files precisam refletir conteúdo, não apenas metadados.

Ignorando nomes

O parâmetro ignore substitui a lista padrão de nomes ignorados.

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

Se você fornece a lista, inclua também o que deseja manter ignorado. Não esconda artefatos relevantes por conveniência.

O parâmetro hide

hide controla nomes omitidos dos relatórios, com padrão incluindo . e ...

Em geral, ignore define itens que não participam da comparação, enquanto hide afeta a apresentação.

Comparação textual com difflib

filecmp informa que dois arquivos diferem, mas não explica como. Para texto, gere um diff depois.

from difflib import unified_diff
from pathlib import Path

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

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

Em arquivos binários, um diff de linhas não faz sentido; reporte tamanho, hash ou uma ferramenta específica do formato.

Quando usar hashes

Uma comparação byte a byte responde se o conteúdo atual coincide. Um hash persistido permite verificar integridade em outro momento ou máquina.

from hashlib import file_digest


def sha256(caminho):
    with open(caminho, "rb") as arquivo:
        return file_digest(arquivo, "sha256").hexdigest()

A documentação oficial de hashlib disponibiliza SHA-256 e outros algoritmos. Para segurança, evite MD5 e SHA-1 em novas verificações contra adulteração.

Hash não elimina a leitura

Calcular um digest lê todos os bytes e normalmente custa mais que uma comparação que pode parar na primeira diferença. Use hashes quando precisa armazenar, transmitir ou assinar uma impressão digital, não apenas para comparar dois arquivos locais uma única vez.

Condições de corrida

Um arquivo pode mudar entre stat(), leitura e geração do relatório. O resultado descreve um momento aproximado, não um snapshot transacional.

Para backups críticos, trabalhe sobre snapshots do sistema de arquivos, bloqueios apropriados ou cópias temporárias. Depois da comparação, verifique novamente metadados se a consistência for essencial.

A resolução de links depende das operações de sistema utilizadas. Uma ferramenta de sincronização precisa definir se preserva links ou compara alvos.

Use Path.is_symlink() e os.readlink() quando a identidade do link importa. Evite seguir links para fora da raiz autorizada.

Arquivos grandes

cmp(..., shallow=False) lê em blocos e não precisa carregar o arquivo inteiro na memória. Mesmo assim, comparar muitos arquivos grandes consome I/O.

Use metadados como filtro inicial, limite concorrência para não saturar o disco e priorize arquivos modificados. Em armazenamento remoto, considere APIs de checksum fornecidas pelo serviço.

Testes com diretórios temporários

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

Inclua arquivos exclusivos, subdiretórios, erros de permissão, links e alterações rápidas.

Exemplo de auditoria estruturada

def auditar(a, b):
    comp = dircmp(a, b, shallow=False)
    return {
        "somente_a": comp.left_only,
        "somente_b": comp.right_only,
        "iguais": comp.same_files,
        "diferentes": comp.diff_files,
        "erros": comp.funny_files + comp.common_funny,
        "subdiretorios": {
            nome: auditar(sub.left, sub.right)
            for nome, sub in comp.subdirs.items()
        },
    }

Em árvores não confiáveis, imponha limite de profundidade e quantidade de itens.

Erros frequentes

  • Usar shallow=True como prova criptográfica de igualdade.
  • Ignorar left_only e right_only.
  • Tratar arquivos com erro como diferentes comuns.
  • Esquecer o cache após gravações muito rápidas.
  • Comparar links simbólicos sem definir a política.
  • Calcular hash para tudo sem considerar custo.
  • Presumir que a árvore não muda durante a análise.
  • Imprimir relatórios quando a aplicação precisa de dados estruturados.

Boas práticas

  • Escolha conscientemente comparação superficial ou por conteúdo.
  • Use cmpfiles() para lotes com nomes conhecidos.
  • Use dircmp para estrutura e recursão.
  • Classifique ausências, diferenças e erros separadamente.
  • Use difflib para explicar diferenças textuais.
  • Use SHA-256 quando precisa de uma impressão digital persistente.
  • Limite profundidade, número de arquivos e concorrência.
  • Teste alterações rápidas e comportamento multiplataforma.

Conclusão

O módulo filecmp no Python fornece uma base simples para comparar arquivos e diretórios. cmp() trabalha com dois arquivos, cmpfiles() classifica um lote e dircmp revela diferenças estruturais e recursivas entre árvores.

A confiabilidade depende da opção escolhida. Metadados oferecem velocidade; conteúdo oferece uma confirmação mais forte; hashes permitem persistir uma impressão digital. Ao separar diferenças de erros, considerar o cache, definir políticas para links e lidar com concorrência, você pode construir verificadores de implantação, backups e auditorias que produzem resultados claros sem ler mais dados do que o necessário.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Documento e caixa de entrada representando caixas de e-mail com mailbox no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    mailbox no Python: caixas de e-mail

    Aprenda mailbox no Python para ler, criar e migrar caixas Maildir, mbox e MH com locking, mensagens, flags e tratamento

    Ler mais

    Tempo de leitura: 6 minutos
    12/08/2026
    Editor de texto representando formatação com textwrap no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    textwrap no Python: formate textos

    Aprenda textwrap no Python para quebrar, preencher, encurtar, indentar e remover recuos de textos com controle de largura e espaços.

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Pasta e lupa representando filtros de nomes com fnmatch no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    fnmatch no Python: filtre nomes de arquivos

    Aprenda fnmatch no Python para filtrar nomes de arquivos com curingas, controlar maiúsculas, excluir padrões e evitar confundir glob com

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Monitor com dados binários representando arrays numéricos compactos no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    array no Python: números compactos

    Aprenda array no Python para armazenar números compactos, manipular bytes, arquivos binários e buffers com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    10/08/2026
    Círculo cromático representando conversões RGB, HSV e HLS com colorsys no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    colorsys no Python: RGB, HSV e HLS

    Aprenda colorsys no Python para converter cores entre RGB, HSV, HLS e YIQ, gerar paletas e evitar erros com escalas

    Ler mais

    Tempo de leitura: 6 minutos
    09/08/2026
    Ícone de configuração representando arquivos plist com plistlib no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    plistlib no Python: arquivos plist

    Aprenda plistlib no Python para ler e gravar arquivos plist XML e binários, validar dados e integrar configurações Apple com

    Ler mais

    Tempo de leitura: 7 minutos
    08/08/2026