O módulo fnmatch da biblioteca padrão compara nomes de arquivos com padrões semelhantes aos curingas usados em shells Unix. Ele é pequeno, rápido e útil quando uma aplicação já possui uma lista de nomes e precisa selecionar itens como *.py, relatorio-202?.csv ou imagem-[0-9].png. Diferentemente de glob, o módulo não percorre diretórios. Diferentemente de re, seus padrões não são expressões regulares.
Neste guia você aprenderá a usar fnmatch(), fnmatchcase(), filter(), filterfalse() e translate(), além de entender portabilidade, desempenho, segurança e limites.
Como os curingas funcionam
Os quatro elementos principais são simples: * corresponde a qualquer quantidade de caracteres; ? corresponde a exatamente um caractere; [abc] aceita um caractere da sequência; e [!abc] rejeita os caracteres indicados.
import fnmatch
print(fnmatch.fnmatch("relatorio-2026.csv", "relatorio-*.csv"))
print(fnmatch.fnmatch("foto-7.jpg", "foto-?.jpg"))
print(fnmatch.fnmatch("log-a.txt", "log-[abc].txt"))Para corresponder literalmente a um metacaractere, coloque-o entre colchetes. O padrão [?], por exemplo, procura um ponto de interrogação real.
Filtrar uma lista existente
fnmatch.filter() recebe um iterável de nomes e devolve somente os que correspondem ao padrão. Ele é mais direto e pode ser mais eficiente que repetir a função manualmente.
from fnmatch import filter
nomes = ["app.py", "teste.py", "README.md", "dados.csv"]
python = filter(nomes, "*.py")
print(python)Esse padrão combina bem com os.listdir(), resultados de APIs, registros de banco, nomes dentro de ZIPs e listas recebidas de outro serviço.
Excluir correspondências com filterfalse
No Python 3.14, filterfalse() passou a oferecer o resultado inverso: devolve os nomes que não correspondem. Isso elimina compreensões repetitivas e deixa a intenção mais clara.
from fnmatch import filterfalse
arquivos = ["a.tmp", "b.txt", "c.log", "d.tmp"]
permanentes = filterfalse(arquivos, "*.tmp")
print(permanentes)Em versões anteriores, use uma compreensão com not fnmatch.fnmatch(nome, padrao).
Maiúsculas e minúsculas
fnmatch() aplica os.path.normcase() ao nome e ao padrão. Assim, o comportamento pode variar conforme o sistema operacional. Em ambientes que normalizam maiúsculas, ARQUIVO.TXT pode corresponder a *.txt. Em outros, não.
Quando a regra precisa ser idêntica em Linux, Windows e macOS, use fnmatchcase(). Apesar do nome, ela não transforma o texto: apenas realiza a comparação sensível a maiúsculas sem normalização.
from fnmatch import fnmatchcase
permitido = fnmatchcase("Relatorio.CSV", "*.csv")
print(permitido) # FalseSeparadores de diretório não são especiais
O separador / não recebe tratamento especial em fnmatch. Um asterisco pode atravessar barras presentes na string. Isso é diferente da expansão de caminhos realizada por glob, que trabalha segmento por segmento.
import fnmatch
print(fnmatch.fnmatch("dados/2026/vendas.csv", "*.csv")) # pode ser TrueSe você precisa caminhar pelo sistema de arquivos, combinar diretórios recursivamente ou respeitar segmentos, use pathlib.Path.glob() ou o módulo glob. Para processar uma lista já obtida, fnmatch é adequado.
Arquivos ocultos
Nomes iniciados por ponto não recebem regra especial. O padrão * também pode corresponder a .env ou .gitignore. Portanto, implemente explicitamente a política de arquivos ocultos.
def visivel(nome):
return not nome.startswith(".")
selecionados = [n for n in nomes if visivel(n) and fnmatch.fnmatch(n, "*")]fnmatch não é regex
O padrão *.txt é válido para fnmatch, mas em regex o asterisco modifica o elemento anterior. Da mesma forma, grupos, quantificadores e âncoras de regex não funcionam como esperado aqui. Escolha a linguagem de padrões conforme a necessidade.
Para regras simples de nomes, curingas são mais legíveis. Para validar estrutura complexa, capturar grupos ou impor limites detalhados, use re.
Converter para expressão regular
translate() converte um padrão de shell para uma expressão regular. Isso é útil quando você deseja compilar o resultado, combinar com outras verificações ou inspecionar a regra produzida.
import fnmatch
import re
regex = re.compile(fnmatch.translate("relatorio-*.csv"))
print(bool(regex.match("relatorio-julho.csv")))Não edite cegamente o texto gerado. Trate-o como uma implementação da semântica do padrão, que pode evoluir entre versões.
Cache e desempenho
As funções principais mantêm um cache tipado de expressões compiladas, com tamanho máximo de 32.768 padrões. Repetir os mesmos padrões é eficiente. Porém, receber padrões sempre diferentes de usuários pode reduzir o benefício do cache e aumentar trabalho de compilação.
Em serviços públicos, limite tamanho, quantidade e origem dos padrões. Evite permitir milhões de padrões arbitrários em uma única requisição. Também filtre primeiro por critérios baratos quando a lista for muito grande.
Strings e bytes
As APIs aceitam str ou bytes codificados em ISO-8859-1, mas nome e padrão precisam ter o mesmo tipo. Misturar bytes com string gera erro. Aplicações modernas normalmente devem padronizar tudo como Unicode.
import fnmatch
print(fnmatch.fnmatch(b"dados.csv", b"*.csv"))
# fnmatch.fnmatch(b"dados.csv", "*.csv") # TypeErrorValidação e segurança
fnmatch somente compara texto. Ele não confirma que o arquivo existe, não impede path traversal, não verifica permissões e não garante que o caminho esteja dentro de uma pasta permitida. Nunca use uma correspondência positiva como única autorização para abrir ou excluir um arquivo.
Normalize e resolva o caminho com pathlib, confirme o diretório-base, rejeite componentes inesperados e aplique permissões reais do sistema. Para exclusão em lote, mostre uma prévia e registre o conjunto selecionado.
Exemplo: selecionar logs com política clara
from fnmatch import fnmatchcase
from pathlib import Path
BASE = Path("logs").resolve()
PADROES = ("app-*.log", "worker-*.log")
def selecionar():
resultado = []
for caminho in BASE.iterdir():
if not caminho.is_file():
continue
if any(fnmatchcase(caminho.name, p) for p in PADROES):
resultado.append(caminho)
return resultadoO código compara apenas caminho.name, evitando que diretórios alterem a semântica. A lista de padrões é controlada pela aplicação e a existência do arquivo é verificada separadamente.
Erros frequentes
- Usar curingas em uma lista não normalizada e esperar o mesmo resultado em todos os sistemas.
- Confundir
fnmatchcom regex. - Esperar que
*pare em barras. - Ignorar arquivos ocultos.
- Tratar a correspondência como autorização de acesso.
- Misturar
stre bytes. - Usar
fnmatchpara percorrer diretórios, quandoglobseria mais adequado.
Boas práticas
- Use
fnmatchcase()quando a regra precisar ser portátil. - Compare apenas o nome quando diretórios não fizerem parte da regra.
- Defina explicitamente a política para arquivos ocultos.
- Limite padrões fornecidos por usuários.
- Valide caminhos e permissões separadamente.
- Teste limites, Unicode, maiúsculas e nomes vazios.
- Prefira
filter()oufilterfalse()para listas inteiras.
Conteúdos relacionados
Veja também os guias sobre fileinput, linecache, shlex, filecmp e bisect.
As referências oficiais são a documentação do fnmatch e a documentação do glob.
Conclusão
fnmatch é ideal para comparar nomes com curingas simples, principalmente quando os nomes já estão em memória. A API é pequena, possui cache interno e agora inclui filtragem inversa no Python 3.14. O uso correto depende de distinguir padrões de shell, regex e expansão de caminhos, além de tratar segurança e portabilidade fora da função de correspondência.







