180 votes

Lister la structure arborescente d'un répertoire en python ?

Je sais que nous pouvons utiliser os.walk() pour lister tous les sous-répertoires ou tous les fichiers d'un répertoire. Cependant, j'aimerais lister le contenu complet de l'arborescence d'un répertoire :

- Subdirectory 1:
   - file11
   - file12
   - Sub-sub-directory 11:
         - file111
         - file112
- Subdirectory 2:
    - file21
    - sub-sub-directory 21
    - sub-sub-directory 22    
        - sub-sub-sub-directory 221
            - file 2211

Quelle est la meilleure façon d'y parvenir en Python ?

227voto

dhobbs Points 246

Voici une fonction qui permet de le faire avec un formatage :

import os

def list_files(startpath):
    for root, dirs, files in os.walk(startpath):
        level = root.replace(startpath, '').count(os.sep)
        indent = ' ' * 4 * (level)
        print('{}{}/'.format(indent, os.path.basename(root)))
        subindent = ' ' * 4 * (level + 1)
        for f in files:
            print('{}{}'.format(subindent, f))

1 votes

Cela a très bien fonctionné, merci. Bien que la plupart des gens le sachent, pour les nouveaux venus en Python, veuillez noter que vous devrez appeler la fonction à la fin (en supposant qu'il s'agisse de Windows), donc vous pourriez ajouter une nouvelle ligne à la fin avec le contenu list_files (" D:\\ ")

1 votes

Fonctionne bien avec python3. Mais sur python2 ValueError: zero length field name in format est lancé.

6 votes

Si startpath est répété dans Root, ne remplacera-t-il pas chaque occurrence ? En passant à root.replace(startpath, '', 1) devrait corriger cela

81voto

abstrus Points 742

Similaire aux réponses ci-dessus, mais pour python3, sans doute lisible et sans doute extensible :

from pathlib import Path

class DisplayablePath(object):
    display_filename_prefix_middle = ''
    display_filename_prefix_last = ''
    display_parent_prefix_middle = '    '
    display_parent_prefix_last = '   '

    def __init__(self, path, parent_path, is_last):
        self.path = Path(str(path))
        self.parent = parent_path
        self.is_last = is_last
        if self.parent:
            self.depth = self.parent.depth + 1
        else:
            self.depth = 0

    @property
    def displayname(self):
        if self.path.is_dir():
            return self.path.name + '/'
        return self.path.name

    @classmethod
    def make_tree(cls, root, parent=None, is_last=False, criteria=None):
        root = Path(str(root))
        criteria = criteria or cls._default_criteria

        displayable_root = cls(root, parent, is_last)
        yield displayable_root

        children = sorted(list(path
                               for path in root.iterdir()
                               if criteria(path)),
                          key=lambda s: str(s).lower())
        count = 1
        for path in children:
            is_last = count == len(children)
            if path.is_dir():
                yield from cls.make_tree(path,
                                         parent=displayable_root,
                                         is_last=is_last,
                                         criteria=criteria)
            else:
                yield cls(path, displayable_root, is_last)
            count += 1

    @classmethod
    def _default_criteria(cls, path):
        return True

    @property
    def displayname(self):
        if self.path.is_dir():
            return self.path.name + '/'
        return self.path.name

    def displayable(self):
        if self.parent is None:
            return self.displayname

        _filename_prefix = (self.display_filename_prefix_last
                            if self.is_last
                            else self.display_filename_prefix_middle)

        parts = ['{!s} {!s}'.format(_filename_prefix,
                                    self.displayname)]

        parent = self.parent
        while parent and parent.parent is not None:
            parts.append(self.display_parent_prefix_middle
                         if parent.is_last
                         else self.display_parent_prefix_last)
            parent = parent.parent

        return ''.join(reversed(parts))

Exemple d'utilisation :

paths = DisplayablePath.make_tree(
    Path('doc'),
    criteria=is_not_hidden
)
for path in paths:
    print(path.displayable())

# With a criteria (skip hidden files)
def is_not_hidden(path):
    return not path.name.startswith(".")

paths = DisplayablePath.make_tree(Path('doc'))
for path in paths:
    print(path.displayable())

Exemple de sortie :

doc/
 _static/
    embedded/
       deep_file
       very/
           deep/
               folder/
                   very_deep_file
    less_deep_file
 about.rst
 conf.py
 index.rst

Notes

  • Il s'agit d'une récursivité. Il lèvera un Erreur de récursivité sur vraiment profond arborescences de dossiers
  • L'arbre est évalué paresseusement. Il devrait se comporter correctement sur des large les arborescences de dossiers. Les enfants immédiats d'un dossier donné ne sont cependant pas évalués paresseusement.

Editer :

  • En prime, un rappel de critères pour filtrer les chemins.

0 votes

Bel outil, avez-vous un exemple rapide sur la façon d'utiliser des critères afin d'exclure des noms de dossiers ?

0 votes

C'est exactement ce que je cherchais. Merci beaucoup !

2 votes

DisplayablePath.make_tree(Path('doc'), criteria=lambda path: True if path.name not in ('.git', '__pycache__') else False)) est un exemple de filtrage des chemins indésirables.

33voto

Tom Points 5832

Il existe un paquet (que j'ai créé) appelé seedir pour faire ceci et d'autres choses avec les diagrammes d'arborescence de dossiers :

>>> import seedir as sd
>>> sd.seedir('/path/to/some/path/or/package', style='emoji')
 package/
 __init__.py
 subpackage1/
  __init__.py
  moduleX.py
  moduleY.py
 subpackage2/
  __init__.py
  moduleZ.py
 moduleA.py

Il est possible de faire quelque chose de similaire au style utilisé par l'OP :

>>> sd.seedir('/path/to/folder', style='spaces', indent=4, anystart='- ')
- package/
    - __init__.py
    - subpackage1/
        - __init__.py
        - moduleX.py
        - moduleY.py
    - subpackage2/
        - __init__.py
        - moduleZ.py
    - moduleA.py

Il existe également une interface en ligne de commande. Il existe également une interface de ligne de commande :

python -m seedir.command_line -y emoji

Pour toutes les options, voir

python -m seedir.command_line --help

0 votes

Est-il possible d'exporter vers une image .png ?

1 votes

@israteneda actuellement non, mais cela pourrait être ajouté !

0 votes

Ce paquet fonctionne-t-il également sous Windows ?

31voto

Intra Points 569

Une solution sans votre indentation :

for path, dirs, files in os.walk(given_path):
  print path
  for f in files:
    print f

os.walk effectue déjà la recherche descendante et en profondeur que vous recherchez.

Le fait d'ignorer la liste des répertoires permet d'éviter le chevauchement que vous mentionnez.

2 votes

Python dit : NameError: name 'path' is not defined

1 votes

@FrancescoMantovani "path" est la variable contenant le répertoire que vous voulez imprimer, c'est-à-dire r" C:\Users\username\Documents\path "

18voto

Rubén Cabrera Points 455

Je suis venu ici pour chercher la même chose et j'ai utilisé la réponse de Dhobbs. Pour remercier la communauté, j'ai ajouté quelques arguments pour écrire dans un fichier, comme l'a demandé akshay, et j'ai rendu l'affichage des fichiers optionnel pour que ce ne soit pas une sortie trop lourde. J'ai également fait de l'indentation un argument optionnel afin que vous puissiez la modifier, car certains aiment qu'elle soit de 2 et d'autres préfèrent 4.

Utilisation de boucles différentes pour que celle qui n'affiche pas les fichiers ne vérifie pas si elle doit le faire à chaque itération.

J'espère que cela aidera quelqu'un d'autre comme la réponse de Dhobbs m'a aidé. Merci beaucoup.

def showFolderTree(path,show_files=False,indentation=2,file_output=False):
"""
Shows the content of a folder in a tree structure.
path -(string)- path of the root folder we want to show.
show_files -(boolean)-  Whether or not we want to see files listed.
                        Defaults to False.
indentation -(int)- Indentation we want to use, defaults to 2.   
file_output -(string)-  Path (including the name) of the file where we want
                        to save the tree.
"""

tree = []

if not show_files:
    for root, dirs, files in os.walk(path):
        level = root.replace(path, '').count(os.sep)
        indent = ' '*indentation*(level)
        tree.append('{}{}/'.format(indent,os.path.basename(root)))

if show_files:
    for root, dirs, files in os.walk(path):
        level = root.replace(path, '').count(os.sep)
        indent = ' '*indentation*(level)
        tree.append('{}{}/'.format(indent,os.path.basename(root)))    
        for f in files:
            subindent=' ' * indentation * (level+1)
            tree.append('{}{}'.format(subindent,f))

if file_output:
    output_file = open(file_output,'w')
    for line in tree:
        output_file.write(line)
        output_file.write('\n')
else:
    # Default behaviour: print on screen.
    for line in tree:
        print line

0 votes

Je pense que cette réponse ne contribue pas à la réponse déjà acceptée. La seule chose que vous fournissez est un code supplémentaire pour désactiver ou non des fonctionnalités dans la réponse.

6 votes

Vous avez raison, @jason-heine. La réponse acceptée est assez bonne, mais certaines personnes ont demandé comment faire ce genre de choses et je voulais leur donner quelque chose. Je pense que cela ne ferait pas de mal, mais je peux me tromper.

4 votes

Il est en effet utile. Merci beaucoup. Je l'ai utilisé tel quel.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X