os.fwalk en Python: recorre directorios

Publicado el: 05/09/2026
Tempo de leitura: 5 minutos
Carpetas y directorios que representan os.fwalk en Python

os.fwalk es una función de la biblioteca estándar de Python que recorre árboles de directorios. Se parece a os.walk, pero cada iteración también entrega un descriptor de archivo para el directorio actual. Ese descriptor puede utilizarse con el parámetro dir_fd de funciones como os.stat, os.open, os.unlink y os.rename.

Para scripts comunes, os.walk o pathlib suelen ser opciones más simples. os.fwalk resulta especialmente útil cuando el programa realiza operaciones sensibles sobre una estructura que puede cambiar mientras se procesa. Es apropiado para herramientas administrativas, limpiadores, inventarios, scanners, empaquetadores y servicios que manipulan muchos archivos.

Uso básico

import os

for raiz, directorios, archivos, fd_raiz in os.fwalk("datos"):
    print(raiz, fd_raiz)
    for nombre in archivos:
        print("archivo:", nombre)

Cada paso devuelve la ruta actual, una lista modificable de subdirectorios, una lista de archivos y el descriptor del directorio actual. Los nombres pueden resolverse de forma relativa a fd_raiz.

Por qué usar descriptores de directorio

Un patrón habitual construye una ruta completa, verifica el archivo y después ejecuta otra operación sobre esa ruta. Entre ambas acciones, otro proceso puede sustituir una parte del camino. Esta situación se conoce como condición de carrera TOCTOU. Las operaciones relativas a un descriptor no eliminan todos los riesgos, pero reducen la necesidad de volver a resolver la ruta completa.

import os

for raiz, _, archivos, fd_raiz in os.fwalk("temporales"):
    for nombre in archivos:
        info = os.stat(nombre, dir_fd=fd_raiz, follow_symlinks=False)
        if info.st_size == 0:
            os.unlink(nombre, dir_fd=fd_raiz)

El ejemplo elimina archivos vacíos sin reconstruir la ruta completa para cada consulta y eliminación.

El descriptor es temporal

El descriptor devuelto por os.fwalk solo permanece válido hasta el siguiente paso de la iteración. No debe almacenarse para usarlo después. Si necesitas conservarlo, duplícalo con os.dup y cierra la copia al terminar.

import os

for raiz, _, _, fd_raiz in os.fwalk("datos"):
    copia = os.dup(fd_raiz)
    try:
        print(os.listdir(copia))
    finally:
        os.close(copia)

Las fugas de descriptores pueden agotar el límite del proceso, por lo que el bloque try/finally es fundamental.

Recorrido de arriba hacia abajo

El valor predeterminado es topdown=True. El directorio padre aparece antes que sus hijos y la lista directorios puede modificarse para podar la búsqueda.

import os

for raiz, directorios, archivos, fd_raiz in os.fwalk("proyecto", topdown=True):
    directorios[:] = [d for d in directorios if d not in {".git", ".venv", "__pycache__"}]
    print(raiz, len(archivos))

La poda evita llamadas innecesarias al sistema y excluye árboles irrelevantes o costosos.

Recorrido de abajo hacia arriba

Con topdown=False, los hijos se visitan antes que el padre. Este modo es útil para eliminar una estructura porque primero deben borrarse los archivos y subdirectorios.

import os

base = "salida-antigua"
for raiz, directorios, archivos, fd_raiz in os.fwalk(base, topdown=False):
    for nombre in archivos:
        os.unlink(nombre, dir_fd=fd_raiz)
    for nombre in directorios:
        os.rmdir(nombre, dir_fd=fd_raiz)
os.rmdir(base)

El código destructivo debe validar la raíz, gestionar permisos y fallos parciales y nunca aceptar directamente una ruta no confiable. Conviene ofrecer un modo de simulación y un registro de auditoría.

Enlaces simbólicos

follow_symlinks vale False por defecto. Esta configuración evita salir accidentalmente del árbol esperado y reduce el riesgo de ciclos. Si necesitas seguir enlaces, registra los directorios visitados mediante dispositivo e inode.

La política también debe ser explícita en las consultas de metadatos. Con follow_symlinks=False, os.stat inspecciona el propio enlace en lugar de su destino.

Gestión de errores

Los archivos pueden desaparecer, los permisos pueden cambiar y el almacenamiento remoto puede fallar durante el recorrido. Usa onerror para errores al leer directorios y captura excepciones específicas para cada entrada.

import os

def registrar(error):
    print(f"No se pudo acceder a {error.filename}: {error}")

for raiz, _, archivos, fd_raiz in os.fwalk("datos", onerror=registrar):
    for nombre in archivos:
        try:
            info = os.stat(nombre, dir_fd=fd_raiz, follow_symlinks=False)
        except FileNotFoundError:
            continue
        except PermissionError as error:
            registrar(error)
        else:
            print(raiz, nombre, info.st_size)

En un directorio activo, que un archivo desaparezca puede ser un evento normal y no un fallo fatal.

Comparación con os.walk y pathlib

pathlib.Path.rglob ofrece una API orientada a objetos muy clara para automatizaciones comunes. os.walk es conocido y portable. os.fwalk debe elegirse cuando las llamadas relativas a descriptores son una parte importante del diseño. Usarlo sin esa necesidad añade complejidad.

La portabilidad también importa. No todas las plataformas admiten todos los argumentos dir_fd. Consulta os.supports_dir_fd y prueba en los sistemas operativos de destino.

import os

if os.stat in os.supports_dir_fd:
    print("os.stat admite dir_fd")

Crear un inventario

import os
from dataclasses import dataclass

@dataclass
class Elemento:
    ruta: str
    tamano: int
    modo: int

def inventario(base: str) -> list[Elemento]:
    resultado = []
    for raiz, directorios, archivos, fd_raiz in os.fwalk(base, follow_symlinks=False):
        directorios[:] = [d for d in directorios if d != ".git"]
        for nombre in archivos:
            try:
                info = os.stat(nombre, dir_fd=fd_raiz, follow_symlinks=False)
            except (FileNotFoundError, PermissionError):
                continue
            resultado.append(Elemento(os.path.join(raiz, nombre), info.st_size, info.st_mode))
    return resultado

Para árboles enormes, convierte la función en generador en vez de acumular todos los elementos. Envía cada resultado a una base de datos, una cola o un escritor de informes.

Renombrar con descriptores

import os

for raiz, _, archivos, fd_raiz in os.fwalk("entradas"):
    for nombre in archivos:
        if nombre.endswith(".part"):
            definitivo = nombre.removesuffix(".part")
            os.rename(nombre, definitivo, src_dir_fd=fd_raiz, dst_dir_fd=fd_raiz)

Los dos nombres se resuelven dentro del mismo directorio abierto. En producción debes prevenir colisiones y definir cómo recuperar transferencias interrumpidas.

Rendimiento

os.fwalk no elimina la latencia del sistema de archivos. Cada llamada a stat sigue llegando al sistema operativo. Poda pronto, evita metadatos innecesarios y presta especial atención al almacenamiento de red. El número de llamadas suele importar más que las microoptimizaciones en Python.

Un paralelismo ilimitado puede saturar discos y servidores. Limita los workers y diseña tareas idempotentes que puedan repetirse con seguridad.

Estrategias de prueba

Las pruebas deben crear árboles temporales con archivos normales, directorios anidados, archivos vacíos, entradas sin permiso cuando la plataforma lo permita y enlaces simbólicos. Verifica la poda, la eliminación bottom-up, el tratamiento de errores y el cierre de descriptores. Nunca ejecutes pruebas destructivas sobre directorios reales.

Buenas prácticas

Valida la raíz, no sigas enlaces por defecto, duplica descriptores solo cuando sea necesario, cierra cada copia, trata la desaparición transitoria como un evento esperado y registra las acciones destructivas. Prefiere una API más simple cuando dir_fd no aporte una ventaja concreta.

Consulta también las guías de Academify sobre pathlib en Python, el módulo os, shutil y contextlib. Las referencias externas principales son la documentación oficial de os.fwalk y la sección de archivos y directorios.

Conclusión

os.fwalk combina el recorrido recursivo con un descriptor para cada directorio actual. Permite operaciones relativas más precisas, reduce algunas carreras relacionadas con rutas y ofrece mayor control sobre árboles que cambian. No sustituye universalmente a os.walk, pero es una herramienta valiosa cuando la manipulación basada en descriptores mejora la seguridad y la robustez.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Desarrollador revisando código Python y métodos sobrescritos
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    typing.override: valida sobrescrituras de métodos

    Aprende typing.override en Python para validar sobrescrituras, firmas compatibles, herencia y refactorizaciones con análisis estático.

    Ler mais

    Tempo de leitura: 6 minutos
    05/09/2026
    Desarrollador trabajando con colas e hilos en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    queue.SimpleQueue: cola FIFO segura entre hilos

    Aprende queue.SimpleQueue en Python para crear colas FIFO seguras entre hilos, workers y diseños productor-consumidor.

    Ler mais

    Tempo de leitura: 5 minutos
    04/09/2026
    Desarrollador trabajando con enums y código Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    StrEnum en Python: enums como strings

    Aprende StrEnum en Python para crear enums como strings, validar entradas, serializar JSON y organizar APIs y configuraciones.

    Ler mais

    Tempo de leitura: 5 minutos
    04/09/2026
    Carpetas y directorios para contextlib.chdir en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    contextlib.chdir: restaura directorios automáticamente

    Aprende contextlib.chdir en Python para cambiar directorios temporalmente, restaurar rutas y crear pruebas confiables sin errores de estado global.

    Ler mais

    Tempo de leitura: 6 minutos
    03/09/2026
    Monitoreo de rendimiento y ejecución de código Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    sys.monitoring: instrumentación de bajo overhead

    Aprende sys.monitoring en Python para instrumentar ejecución con bajo overhead, eventos selectivos, callbacks y observabilidad segura.

    Ler mais

    Tempo de leitura: 6 minutos
    03/09/2026
    Desarrollador organizando datos con operator.attrgetter en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    operator.attrgetter: ordena objetos por atributos

    Aprende operator.attrgetter en Python para ordenar, agrupar y transformar objetos por atributos simples o anidados con código claro.

    Ler mais

    Tempo de leitura: 4 minutos
    02/09/2026