Comment parcourir récursivement un répertoire avec os.walk en Python

La fonction os.walk de Python est un outil puissant pour parcourir récursivement un répertoire et son contenu. En utilisant cette fonction, vous pouvez facilement récupérer tous les sous-répertoires et fichiers d’un répertoire spécifié. Cet article couvre l’utilisation de base de os.walk, ainsi que des exemples pratiques, afin de vous aider à gérer plus efficacement les tâches impliquant la manipulation des répertoires.

Sommaire

Qu’est-ce que os.walk ?


os.walk est une fonction incluse dans le module standard os de Python. Elle permet de parcourir récursivement un répertoire spécifié et génère une liste des fichiers et sous-répertoires dans ce répertoire. En utilisant cette fonction, vous pouvez explorer facilement des structures de répertoires complexes et obtenir des listes de fichiers ou de dossiers, ce qui est très pratique pour les opérations de gestion de répertoires.

Comment fonctionne os.walk


os.walk fonctionne comme un générateur et renvoie un tuple contenant trois éléments :

  1. Le chemin du répertoire (dirpath)
    Indique le chemin du répertoire actuellement exploré.
  2. La liste des sous-répertoires (dirnames)
    Une liste des noms des sous-répertoires dans le répertoire actuel.
  3. La liste des fichiers (filenames)
    Une liste des noms des fichiers dans le répertoire actuel.

Caractéristiques

  • Parcours récursif: Explore automatiquement les sous-répertoires du répertoire spécifié.
  • Ordre: Il est possible de configurer le parcours pour qu’il soit effectué du haut vers le bas ou du bas vers le haut (topdown=True/False).
  • Efficacité: Génère les informations nécessaires à la volée, ce qui permet une gestion efficace de la mémoire.

Applications

  • Recherche de fichiers par nom
  • Création d’une liste de fichiers avec une extension spécifique
  • Calcul de la taille des sous-répertoires
  • Automatisation des tâches de sauvegarde et de déplacement

Utilisation de base

En utilisant os.walk, vous pouvez facilement obtenir les fichiers et dossiers sous un répertoire spécifié. Voici un exemple de code simple pour illustrer son utilisation de base.

Exemple de code

import os

# Spécification du répertoire cible
target_directory = "/path/to/your/directory"

# Parcours du répertoire avec os.walk
for dirpath, dirnames, filenames in os.walk(target_directory):
    print(f"Chemin actuel: {dirpath}")
    print(f"Sous-répertoires: {dirnames}")
    print(f"Fichiers: {filenames}")
    print("-" * 40)

Exemple de sortie


Supposons que la structure du répertoire soit la suivante :

/path/to/your/directory
├── file1.txt
├── file2.txt
├── subdir1
│   └── file3.txt
└── subdir2
    └── file4.txt

Lorsque vous exécutez os.walk, vous obtiendrez la sortie suivante :

Chemin actuel: /path/to/your/directory
Sous-répertoires: ['subdir1', 'subdir2']
Fichiers: ['file1.txt', 'file2.txt']
----------------------------------------
Chemin actuel: /path/to/your/directory/subdir1
Sous-répertoires: []
Fichiers: ['file3.txt']
----------------------------------------
Chemin actuel: /path/to/your/directory/subdir2
Sous-répertoires: []
Fichiers: ['file4.txt']
----------------------------------------

Explication

  • dirpath: Le chemin du répertoire actuellement exploré.
  • dirnames: La liste des sous-répertoires dans le répertoire actuel.
  • filenames: La liste des fichiers dans le répertoire actuel.

Remarques


Si le répertoire spécifié n’existe pas, os.walk générera une erreur. Il est donc conseillé de vérifier préalablement l’existence du répertoire pour éviter ce type d’erreur.

Traitement des fichiers et répertoires séparément

Avec os.walk, vous pouvez facilement trier les fichiers et les répertoires et appliquer des traitements différents à chacun. Il suffit d’ajouter des conditions de branchement pour réaliser cette distinction.

Exemple de code


Voici un exemple où les fichiers et répertoires sont traités séparément :

import os

# Spécification du répertoire cible
target_directory = "/path/to/your/directory"

# Parcours du répertoire avec traitement séparé des fichiers et des répertoires
for dirpath, dirnames, filenames in os.walk(target_directory):
    # Traitement des sous-répertoires
    for dirname in dirnames:
        subdir_path = os.path.join(dirpath, dirname)
        print(f"Répertoire: {subdir_path}")

    # Traitement des fichiers
    for filename in filenames:
        file_path = os.path.join(dirpath, filename)
        print(f"Fichier: {file_path}")

Exemple de sortie


Supposons que la structure du répertoire soit la suivante :

/path/to/your/directory
├── file1.txt
├── file2.txt
├── subdir1
│   └── file3.txt
└── subdir2
    └── file4.txt

La sortie sera la suivante :

Répertoire: /path/to/your/directory/subdir1
Répertoire: /path/to/your/directory/subdir2
Fichier: /path/to/your/directory/file1.txt
Fichier: /path/to/your/directory/file2.txt
Fichier: /path/to/your/directory/subdir1/file3.txt
Fichier: /path/to/your/directory/subdir2/file4.txt

Explication du code

  • os.path.join: Utilisé pour combiner dirpath et dirname ou filename afin de générer des chemins absolus.
  • Traitement des répertoires (for dirname in dirnames): Vous pouvez appliquer des opérations spécifiques aux répertoires (par exemple, obtenir la date de création d’un répertoire).
  • Traitement des fichiers (for filename in filenames): Vous pouvez appliquer des opérations spécifiques aux fichiers (par exemple, obtenir la taille d’un fichier).

Exemples d’applications

  • Créer et gérer une liste des sous-répertoires.
  • Extraire et traiter des fichiers correspondant à une convention de nommage spécifique.
  • Filtrer les fichiers en fonction de leur taille ou de leur date de création.

Recherche de fichiers avec une extension spécifique

En utilisant os.walk, vous pouvez facilement rechercher des fichiers avec une extension spécifique, comme .txt ou .jpg, et extraire uniquement ces fichiers.

Exemple de code


Voici un exemple de code pour rechercher les fichiers avec l’extension .txt et afficher leur chemin :

import os

# Spécification du répertoire cible
target_directory = "/path/to/your/directory"

# Extension à rechercher
target_extension = ".txt"

# Recherche des fichiers avec l'extension spécifiée
for dirpath, dirnames, filenames in os.walk(target_directory):
    for filename in filenames:
        if filename.endswith(target_extension):
            file_path = os.path.join(dirpath, filename)
            print(f"Trouvé: {file_path}")

Exemple de sortie


Supposons que la structure du répertoire soit la suivante :

/path/to/your/directory
├── file1.txt
├── file2.doc
├── subdir1
│   └── notes.txt
└── subdir2
    └── image.png

La sortie sera la suivante :

Trouvé: /path/to/your/directory/file1.txt
Trouvé: /path/to/your/directory/subdir1/notes.txt

Explication du code

  • filename.endswith(target_extension): Cette condition renvoie True si le nom de fichier se termine par l’extension spécifiée. Cela permet de filtrer les fichiers selon leur format.
  • os.path.join: Utilisé pour générer un chemin absolu pour le fichier.

Recherche de plusieurs extensions


Si vous souhaitez rechercher plusieurs extensions, vous pouvez ajuster la condition en conséquence.

# Extensions à rechercher spécifiées dans une liste
target_extensions = [".txt", ".doc"]

for dirpath, dirnames, filenames in os.walk(target_directory):
    for filename in filenames:
        if filename.endswith(tuple(target_extensions)):
            file_path = os.path.join(dirpath, filename)
            print(f"Trouvé: {file_path}")

Applications pratiques

  • Obtenir la liste des fichiers source dans un projet (par exemple, les fichiers .py).
  • Rechercher et traiter en lot des fichiers d’images spécifiques (par exemple, .jpg ou .png).
  • Créer des statistiques sur les fichiers selon leur extension.

Limitation de la profondeur de parcours du répertoire

os.walk explore tous les niveaux d’un répertoire par défaut, mais dans certains cas, vous pouvez souhaiter limiter la recherche à une profondeur spécifique. Pour ce faire, vous pouvez suivre la profondeur actuelle et limiter le traitement en fonction de celle-ci.

Exemple de code


Voici un exemple où la profondeur du répertoire est limitée à 2 :

import os

# Spécification du répertoire cible
target_directory = "/path/to/your/directory"

# Limitation de la profondeur maximale à 2
max_depth = 2

# Exploration avec limitation de la profondeur
for dirpath, dirnames, filenames in os.walk(target_directory):
    # Calcul de la profondeur actuelle
    current_depth = dirpath.count(os.sep) - target_directory.count(os.sep) + 1

    if current_depth > max_depth:
        # Ignorer les sous-répertoires si la profondeur maximale est dépassée
        del dirnames[:]  # Vider la liste dirnames pour ignorer les sous-répertoires
        continue

    print(f"Profondeur {current_depth}: {dirpath}")
    print(f"Sous-répertoires: {dirnames}")
    print(f"Fichiers: {filenames}")
    print("-" * 40)

Exemple de sortie


Si la structure du répertoire est la suivante :

/path/to/your/directory
├── file1.txt
├── subdir1
│   ├── file2.txt
│   └── subsubdir1
│       └── file3.txt
└── subdir2
    └── file4.txt

Le résultat pour une exploration jusqu’à une profondeur de 2 sera le suivant :

Profondeur 1: /path/to/your/directory
Sous-répertoires: ['subdir1', 'subdir2']
Fichiers: ['file1.txt']
----------------------------------------
Profondeur 2: /path/to/your/directory/subdir1
Sous-répertoires: ['subsubdir1']
Fichiers: ['file2.txt']
----------------------------------------
Profondeur 2: /path/to/your/directory/subdir2
Sous-répertoires: []
Fichiers: ['file4.txt']
----------------------------------------

Explication du code

  • os.sep: Permet de récupérer le séparateur de répertoire propre au système d’exploitation (par exemple, \\ sous Windows et / sous Unix).
  • dirpath.count(os.sep): Compte le nombre de séparateurs de répertoire dans le chemin actuel, ce qui permet de calculer la profondeur du répertoire.
  • del dirnames[:]: Vide la liste des sous-répertoires afin d’arrêter l’exploration des sous-répertoires plus profonds.

Applications pratiques

  • Explorer uniquement les répertoires de niveau supérieur dans un grand projet.
  • Afficher une partie limitée de l’arborescence des répertoires.
  • Réduire la charge des opérations disque en limitant l’exploration.

Ignorer les fichiers et répertoires cachés

Lorsque vous parcourez des répertoires, vous pouvez avoir besoin d’ignorer les fichiers et répertoires cachés, généralement ceux qui commencent par un point .. En filtrant ces éléments avec os.walk, vous pouvez effectuer des traitements plus efficaces.

Exemple de code


Voici un exemple de code pour ignorer les fichiers et répertoires cachés lors du parcours d’un répertoire :

import os

# Spécification du répertoire cible
target_directory = "/path/to/your/directory"

# Parcours en ignorant les fichiers et répertoires cachés
for dirpath, dirnames, filenames in os.walk(target_directory):
    # Ignorer les répertoires cachés
    dirnames[:] = [d for d in dirnames if not d.startswith(".")]

    # Ignorer les fichiers cachés
    visible_files = [f for f in filenames if not f.startswith(".")]

    print(f"Chemin actuel: {dirpath}")
    print(f"Sous-répertoires: {dirnames}")
    print(f"Fichiers: {visible_files}")
    print("-" * 40)

Exemple de sortie


Si la structure du répertoire est la suivante :

/path/to/your/directory
├── file1.txt
├── .hidden_file.txt
├── subdir1
│   ├── file2.txt
│   └── .hidden_folder
│       └── file3.txt
└── subdir2
    └── file4.txt

Les fichiers et répertoires cachés seront ignorés, et la sortie sera la suivante :

Chemin actuel: /path/to/your/directory
Sous-répertoires: ['subdir1', 'subdir2']
Fichiers: ['file1.txt']
----------------------------------------
Chemin actuel: /path/to/your/directory/subdir1
Sous-répertoires: []
Fichiers: ['file2.txt']
----------------------------------------
Chemin actuel: /path/to/your/directory/subdir2
Sous-répertoires: []
Fichiers: ['file4.txt']
----------------------------------------

Explication du code

  • Ignorer les répertoires cachés (dirnames[:] = ...): Remplace dirnames par une liste des répertoires dont les noms ne commencent pas par un point, ce qui empêche l’exploration de ces répertoires.
  • Ignorer les fichiers cachés ([f for f in filenames ...]): Utilise une compréhension de liste pour exclure les fichiers dont les noms commencent par un point.

Remarques

  • Si les fichiers ou répertoires sont définis comme « cachés » par des attributs système (surtout sous Windows), ce code ne pourra pas les détecter. Dans ce cas, vous devrez utiliser des modules supplémentaires (par exemple ctypes).

Applications pratiques

  • Exclure les fichiers de configuration cachés (par exemple, .gitignore ou .env).
  • Créer une liste de répertoires visibles pour les utilisateurs.
  • Nettoyer des jeux de données volumineux en excluant les éléments cachés.

Calcul de la taille d’un répertoire

En utilisant os.walk, vous pouvez calculer la taille totale d’un répertoire en additionnant la taille de tous les fichiers qu’il contient. Cette méthode est utile pour connaître l’espace de stockage utilisé par un dossier particulier.

Exemple de code


Voici un exemple de code pour calculer la taille totale d’un répertoire :

import os

# Spécification du répertoire cible
target_directory = "/path/to/your/directory"

def calculate_directory_size(directory):
    total_size = 0
    # Parcours du répertoire
    for dirpath, dirnames, filenames in os.walk(directory):
        for filename in filenames:
            file_path = os.path.join(dirpath, filename)
            try:
                # Ajout de la taille du fichier
                total_size += os.path.getsize(file_path)
            except FileNotFoundError:
                # Gestion des exceptions si un fichier est supprimé
                pass
    return total_size

# Calcul de la taille du répertoire
directory_size = calculate_directory_size(target_directory)

# Affichage du résultat (en octets et en Mo)
print(f"Taille du répertoire: {directory_size} octets")
print(f"Taille du répertoire: {directory_size / (1024 ** 2):.2f} Mo")

Exemple de sortie


Si la structure du répertoire est la suivante :

/path/to/your/directory
├── file1.txt (500 octets)
├── subdir1
│   ├── file2.txt (1500 octets)
│   └── file3.txt (3000 octets)
└── subdir2
    └── file4.txt (2000 octets)

Le résultat de l’exécution sera :

Taille du répertoire: 7000 octets
Taille du répertoire: 0.01 Mo

Explication du code

  • os.path.getsize(file_path): Permet d’obtenir la taille d’un fichier en octets.
  • Gestion des exceptions: Si un fichier est supprimé ou inaccessible, l’exception FileNotFoundError est capturée pour éviter que le programme ne plante.
  • Conversion des unités: Convertit la taille en octets en une taille plus lisible, par exemple en Mo.

Calcul de la taille des fichiers avec une extension spécifique


Pour calculer la taille totale des fichiers avec une extension spécifique, vous pouvez modifier le code comme suit :

def calculate_size_by_extension(directory, extension):
    total_size = 0
    for dirpath, dirnames, filenames in os.walk(directory):
        for filename in filenames:
            if filename.endswith(extension):
                file_path = os.path.join(dirpath, filename)
                try:
                    total_size += os.path.getsize(file_path)
                except FileNotFoundError:
                    pass
    return total_size

# Exemple : calcul de la taille totale des fichiers .txt
txt_size = calculate_size_by_extension(target_directory, ".txt")
print(f"Taille des fichiers .txt: {txt_size} octets")

Utilité pratique

  • Suivi de l’utilisation de l’espace disque sur un serveur ou un stockage cloud.
  • Estimation de la consommation des ressources d’un dossier projet spécifique.
  • Assistance pour le nettoyage lors d’un manque d’espace disque.

Création d’une copie de sauvegarde de tous les fichiers

En utilisant os.walk, vous pouvez parcourir récursivement tous les fichiers d’un répertoire et les copier dans un répertoire de sauvegarde, créant ainsi une copie de sauvegarde complète du répertoire tout en préservant sa structure.

Exemple de code


Voici un exemple de code pour copier tous les fichiers dans un répertoire de sauvegarde :

import os
import shutil

# Répertoires source et de sauvegarde spécifiés
source_directory = "/path/to/your/source_directory"
backup_directory = "/path/to/your/backup_directory"

def backup_files(source, backup):
    for dirpath, dirnames, filenames in os.walk(source):
        # Calcul du chemin de sauvegarde
        relative_path = os.path.relpath(dirpath, source)
        backup_path = os.path.join(backup, relative_path)

        # Création du répertoire de sauvegarde
        os.makedirs(backup_path, exist_ok=True)

        for filename in filenames:
            source_file = os.path.join(dirpath, filename)
            backup_file = os.path.join(backup_path, filename)

            try:
                # Copie du fichier
                shutil.copy2(source_file, backup_file)
                print(f"Copié: {source_file} -> {backup_file}")
            except Exception as e:
                print(f"Échec de la copie de {source_file}: {e}")

# Exécution de la sauvegarde
backup_files(source_directory, backup_directory)

Exemple de sortie


Si la structure du répertoire source est la suivante :

/path/to/your/source_directory
├── file1.txt
├── subdir1
│   ├── file2.txt
│   └── file3.txt
└── subdir2
    └── file4.txt

La structure du répertoire de sauvegarde sera la suivante :

/path/to/your/backup_directory
├── file1.txt
├── subdir1
│   ├── file2.txt
│   └── file3.txt
└── subdir2
    └── file4.txt

Explication du code

  • os.makedirs(backup_path, exist_ok=True): Crée le répertoire de sauvegarde de manière récursive. Le paramètre exist_ok=True permet de ne pas générer d’erreur si le répertoire existe déjà.
  • os.path.relpath(dirpath, source): Récupère le chemin relatif du répertoire source pour créer le répertoire de sauvegarde correspondant.
  • shutil.copy2(source_file, backup_file): Copie le fichier ainsi que ses métadonnées, comme les timestamps.

Remarques

  1. Liens symboliques: shutil.copy2 copie les liens symboliques comme des fichiers normaux. Si vous souhaitez conserver les liens, un traitement supplémentaire est nécessaire.
  2. Capacité disque: Assurez-vous que le répertoire de sauvegarde dispose de suffisamment d’espace disque.
  3. Permissions d’accès: Vous devez avoir les permissions nécessaires pour accéder aux fichiers source.

Applications pratiques

  • Sauvegarde de fichiers spécifiques, comme ceux avec une extension particulière (par exemple, if filename.endswith(".txt")).
  • Création d’un journal de sauvegarde: enregistrer les fichiers copiés dans un fichier journal.
  • Sauvegarde différentielle: Copiez uniquement les fichiers modifiés en comparant les timestamps ou les hachages.

Résumé

Dans cet article, nous avons exploré comment utiliser os.walk pour parcourir récursivement un répertoire et ses fichiers en Python. Nous avons abordé son utilisation de base, ainsi que de nombreuses applications pratiques, telles que la recherche de fichiers par extension, le calcul de la taille des répertoires, l’ignorance des fichiers et répertoires cachés, et la création de sauvegardes automatiques.

Grâce à sa flexibilité, os.walk est un outil puissant pour automatiser les tâches liées à la gestion des répertoires et améliorer l’efficacité du travail.

Sommaire