El módulo fnmatch de la biblioteca estándar compara nombres de archivos con patrones de comodines similares a los usados por shells Unix. Es útil cuando una aplicación ya dispone de una colección de nombres y necesita seleccionar elementos como *.py, informe-202?.csv o imagen-[0-9].png. A diferencia de glob, no recorre directorios. A diferencia de re, sus patrones no son expresiones regulares.
Esta guía explica fnmatch(), fnmatchcase(), filter(), filterfalse() y translate(), junto con prácticas de portabilidad, rendimiento y seguridad.
Sintaxis de los comodines
La sintaxis tiene cuatro construcciones principales. El asterisco coincide con cualquier cantidad de caracteres, el signo de interrogación con exactamente uno, [abc] acepta un carácter de la secuencia y [!abc] excluye los indicados.
import fnmatch
print(fnmatch.fnmatch("informe-2026.csv", "informe-*.csv"))
print(fnmatch.fnmatch("foto-7.jpg", "foto-?.jpg"))
print(fnmatch.fnmatch("log-a.txt", "log-[abc].txt"))Para buscar literalmente un metacarácter, colócalo entre corchetes. El patrón [?], por ejemplo, coincide con un signo de interrogación real.
Filtrar una colección existente
fnmatch.filter() recibe un iterable de nombres y devuelve solamente los que coinciden. Expresa la intención con claridad y está implementado de forma más eficiente que repetir manualmente la misma comparación.
from fnmatch import filter
nombres = ["app.py", "test.py", "README.md", "datos.csv"]
python = filter(nombres, "*.py")
print(python)El patrón funciona con os.listdir(), resultados de APIs, registros de bases de datos, entradas de archivos comprimidos, claves de almacenamiento y listas recibidas de otro servicio.
Excluir coincidencias con filterfalse
Python 3.14 añadió filterfalse(), que devuelve los nombres que no coinciden. Así se evitan comprensiones negadas repetitivas y las políticas de exclusión quedan más claras.
from fnmatch import filterfalse
archivos = ["a.tmp", "b.txt", "c.log", "d.tmp"]
permanentes = filterfalse(archivos, "*.tmp")
print(permanentes)En versiones anteriores, usa una comprensión con not fnmatch.fnmatch(nombre, patron).
Mayúsculas según el sistema operativo
fnmatch() aplica os.path.normcase() al nombre y al patrón. Por eso el resultado puede cambiar entre sistemas. En plataformas que normalizan mayúsculas, ARCHIVO.TXT puede coincidir con *.txt; en sistemas sensibles a mayúsculas normalmente no.
Usa fnmatchcase() cuando la política deba comportarse igual en Linux, Windows, macOS, contenedores y CI. Realiza una comparación sensible a mayúsculas sin normalización del sistema.
from fnmatch import fnmatchcase
permitido = fnmatchcase("Informe.CSV", "*.csv")
print(permitido) # FalseLos separadores de directorio son caracteres normales
La barra no recibe tratamiento especial en fnmatch. Un asterisco puede atravesar separadores presentes en la cadena. Esto difiere de la expansión de rutas, donde glob procesa segmentos.
import fnmatch
print(fnmatch.fnmatch("datos/2026/ventas.csv", "*.csv"))Usa pathlib.Path.glob() o glob para recorrer el sistema de archivos, aplicar recursividad o respetar segmentos. Usa fnmatch cuando ya tienes los nombres y solo necesitas comparación textual.
Archivos ocultos
Los nombres que comienzan con punto no son especiales. Un patrón como * puede coincidir con .env, .gitignore y otros archivos ocultos. La aplicación debe definir esta política explícitamente.
def visible(nombre):
return not nombre.startswith(".")
seleccionados = [n for n in nombres if visible(n) and fnmatch.fnmatch(n, "*")]fnmatch no es regex
El patrón *.txt es válido en fnmatch, mientras que en una expresión regular el asterisco modifica el token anterior. Los grupos, capturas, lookarounds y cuantificadores numéricos de regex no están disponibles en los comodines.
Los comodines suelen ser mejores para reglas legibles sobre nombres. Usa re cuando necesites capturar grupos, imponer estructura compleja o validar límites precisos.
Traducir el patrón a regex
translate() convierte la sintaxis de shell en una expresión regular. Puede servir para compilar el resultado, integrarlo en otra etapa o inspeccionar la regla generada.
import fnmatch
import re
regex = re.compile(fnmatch.translate("informe-*.csv"))
print(bool(regex.match("informe-julio.csv")))Trata la cadena generada como detalle de implementación. No dependas de su representación exacta entre versiones de Python.
Caché y rendimiento
Las funciones principales mantienen una caché tipada de expresiones compiladas con un máximo de 32.768 patrones. Reutilizar un conjunto pequeño y estable es eficiente. En cambio, recibir patrones únicos de usuarios continuamente reduce el beneficio de la caché.
En servicios públicos limita longitud y cantidad de patrones. Evita comparar colecciones enormes contra miles de reglas arbitrarias en una sola solicitud. Aplica filtros baratos primero, pagina resultados y mide cargas reales.
Cadenas y bytes
Las APIs aceptan cadenas Unicode o bytes codificados en ISO-8859-1, pero nombre y patrón deben tener el mismo tipo. Mezclar bytes con str genera error. Las aplicaciones modernas normalmente deben normalizar todo a Unicode.
import fnmatch
print(fnmatch.fnmatch(b"datos.csv", b"*.csv"))
# fnmatch.fnmatch(b"datos.csv", "*.csv") # TypeErrorLímites de seguridad
fnmatch solamente compara texto. No confirma que el archivo exista, no evita path traversal, no impone permisos y no garantiza que una ruta permanezca dentro de un directorio autorizado. Una coincidencia positiva nunca debe ser la única autorización antes de leer, mover, subir o borrar.
Resuelve rutas con pathlib, verifica el directorio base, rechaza componentes inesperados y aplica controles de acceso reales. Para operaciones destructivas por lotes, muestra una vista previa y registra los nombres seleccionados.
Ejemplo: seleccionar logs
from fnmatch import fnmatchcase
from pathlib import Path
BASE = Path("logs").resolve()
PATRONES = ("app-*.log", "worker-*.log")
def seleccionar_logs():
resultado = []
for ruta in BASE.iterdir():
if not ruta.is_file():
continue
if any(fnmatchcase(ruta.name, p) for p in PATRONES):
resultado.append(ruta)
return resultadoEl código compara únicamente ruta.name, por lo que los directorios no alteran la semántica. La aplicación controla los patrones y verifica por separado que cada resultado sea un archivo.
Errores frecuentes
- Esperar el mismo comportamiento de mayúsculas en todos los sistemas.
- Confundir comodines con expresiones regulares.
- Suponer que el asterisco se detiene en las barras.
- Olvidar que los archivos ocultos coinciden con patrones normales.
- Usar una coincidencia como decisión de autorización.
- Mezclar cadenas y bytes.
- Usar
fnmatchpara recorrer directorios en lugar deglob.
Buenas prácticas
- Elige
fnmatchcase()para políticas portables. - Compara solo el nombre base cuando los directorios no importen.
- Define explícitamente el tratamiento de archivos ocultos.
- Limita patrones proporcionados por usuarios.
- Valida rutas y permisos por separado.
- Prueba Unicode, mayúsculas, nombres vacíos y límites.
- Usa
filter()yfilterfalse()para colecciones completas.
Guías relacionadas
Continúa con nuestras guías de fileinput en Python, linecache en Python, shlex en Python, filecmp en Python y bisect en Python.
Las referencias oficiales son la documentación de fnmatch y la documentación de glob.
Conclusión
fnmatch es una solución enfocada para comparar nombres existentes con reglas de comodines legibles. Su API pequeña, caché interna y filtrado inverso de Python 3.14 lo hacen útil para filtros y políticas. El uso correcto exige separar la coincidencia de la expansión de rutas, regex, autorización y validación de caminos.







