Structure arborescente de répertoire de liste 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, je voudrais lister le contenu complet de l'arborescence des répertoires:

  • sous-répertoire 1:
    • fichier11
    • file12
    • sous-sous-répertoire 11:
      • file111
      • file112
  • sous-répertoire 2:
    • fichier21
    • sous-sous-répertoire 21
    • sous-sous-sous-répertoire 22
      • sous-sous-sous-sous-répertoire 221
        • fichier 2211

Comment y parvenir au mieux en Python?

49
demandé sur albfan 2012-03-16 00:29:30

9 réponses

Voici une fonction pour le faire avec le 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))
81
répondu dhobbs 2012-03-16 03:14:08

Une solution sans votre indentation:

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

Os.walk fait déjà le top-down, profondeur-première marche que vous recherchez.

Ignorer la liste dirs empêche le chevauchement que vous mentionnez.

17
répondu Intra 2012-03-16 15:41:57

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

Utilisé différentes boucles de sorte que celui qui ne montre pas les fichiers ne vérifie pas si elle doit à chaque itération.

J'espère que cela aide quelqu'un d'autre comme la réponse de dhobbs m'a aidé. Mille mercis.

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
8
répondu Rubén Cabrera 2017-05-26 04:49:10

Basé sur ce fantastique post

Http://code.activestate.com/recipes/217212-treepy-graphically-displays-the-directory-structur/

Voici un raffinement pour se comporter exactement comme

Http://linux.die.net/man/1/tree

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

# tree.py
#
# Written by Doug Dahms
#
# Prints the tree structure for the path specified on the command line

from os import listdir, sep
from os.path import abspath, basename, isdir
from sys import argv

def tree(dir, padding, print_files=False, isLast=False, isFirst=False):
    if isFirst:
        print padding.decode('utf8')[:-1].encode('utf8') + dir
    else:
        if isLast:
            print padding.decode('utf8')[:-1].encode('utf8') + '└── ' + basename(abspath(dir))
        else:
            print padding.decode('utf8')[:-1].encode('utf8') + '├── ' + basename(abspath(dir))
    files = []
    if print_files:
        files = listdir(dir)
    else:
        files = [x for x in listdir(dir) if isdir(dir + sep + x)]
    if not isFirst:
        padding = padding + '   '
    files = sorted(files, key=lambda s: s.lower())
    count = 0
    last = len(files) - 1
    for i, file in enumerate(files):
        count += 1
        path = dir + sep + file
        isLast = i == last
        if isdir(path):
            if count == len(files):
                if isFirst:
                    tree(path, padding, print_files, isLast, False)
                else:
                    tree(path, padding + ' ', print_files, isLast, False)
            else:
                tree(path, padding + '│', print_files, isLast, False)
        else:
            if isLast:
                print padding + '└── ' + file
            else:
                print padding + '├── ' + file

def usage():
    return '''Usage: %s [-f] 
Print tree structure of path specified.
Options:
-f      Print files as well as directories
PATH    Path to process''' % basename(argv[0])

def main():
    if len(argv) == 1:
        print usage()
    elif len(argv) == 2:
        # print just directories
        path = argv[1]
        if isdir(path):
            tree(path, '', False, False, True)
        else:
            print 'ERROR: \'' + path + '\' is not a directory'
    elif len(argv) == 3 and argv[1] == '-f':
        # print directories and files
        path = argv[2]
        if isdir(path):
            tree(path, '', True, False, True)
        else:
            print 'ERROR: \'' + path + '\' is not a directory'
    else:
        print usage()

if __name__ == '__main__':
    main()


4
répondu albfan 2016-03-27 23:07:16

Vous pouvez exécuter la commande 'tree' du shell Linux.

Installation:

   ~$sudo apt install tree

Utilisation en python

    >>> import os
    >>> os.system('tree <desired path>')

Exemple:

    >>> os.system('tree ~/Desktop/myproject')

Cela vous donne une structure plus propre et est visuellement plus complet et facile à taper.

2
répondu Ashfaq Ur Rahman N 2018-04-03 01:12:27

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'))
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

  • cela utilise la récursivité. Il va déclencher une RecursionError sur les arbres de dossiers vraiment profonds
  • L'arbre est paresseusement évalué. Il devrait bien se comporter sur les arbres de dossiers vraiment larges. Les enfants immédiats d'un dossier donné ne sont pas évalués paresseusement, bien.

Modifier:

  • bonus ajouté! Rappel de critères pour les chemins de filtrage.
2
répondu abstrus 2018-04-19 04:24:10

Au-dessus de dhobbs réponse ci-dessus ( https://stackoverflow.com/a/9728478/624597 ), Voici une fonctionnalité supplémentaire de stockage des résultats dans un fichier (je l'utilise personnellement pour copier et coller dans FreeMind pour avoir un bon aperçu de la structure, donc j'ai utilisé des onglets au lieu d'espaces pour l'indentation):

import os

def list_files(startpath):

    with open("folder_structure.txt", "w") as f_output:
        for root, dirs, files in os.walk(startpath):
            level = root.replace(startpath, '').count(os.sep)
            indent = '\t' * 1 * (level)
            output_string = '{}{}/'.format(indent, os.path.basename(root))
            print(output_string)
            f_output.write(output_string + '\n')
            subindent = '\t' * 1 * (level + 1)
            for f in files:
                output_string = '{}{}'.format(subindent, f)
                print(output_string)
                f_output.write(output_string + '\n')

list_files(".")
1
répondu ellockie 2017-05-23 12:18:24

Peut-être plus rapide que @ ellockie (peut-être )

import os
def file_writer(text):
    with open("folder_structure.txt","a") as f_output:
        f_output.write(text)
def list_files(startpath):


    for root, dirs, files in os.walk(startpath):
        level = root.replace(startpath, '').count(os.sep)
        indent = '\t' * 1 * (level)
        output_string = '{}{}/ \n'.format(indent, os.path.basename(root))
        file_writer(output_string)
        subindent = '\t' * 1 * (level + 1)
        output_string = '%s %s \n' %(subindent,[f for f in files])
        file_writer(''.join(output_string))


list_files("/")

Edit: j'ai été testé capture d'écran est que:

entrez la description de l'image ici

1
répondu Enes ERGUN 2016-11-15 08:15:50
import os

def fs_tree_to_dict(path_):
    file_token = ''
    for root, dirs, files in os.walk(path_):
        tree = {d: fs_tree_to_dict(os.path.join(root, d)) for d in dirs}
        tree.update({f: file_token for f in files})
        return tree  # note we discontinue iteration trough os.walk

Si quelqu'un est intéressé - cette fonction récursive renvoie la structure imbriquée des dictionnaires. Les clés sont file system noms (des répertoires et des fichiers), les valeurs sont:

  • sous-dictionnaires pour les répertoires
  • chaînes pour les fichiers (voir file_token)

Les chaînes désignant les fichiers sont vides dans cet exemple. Ils peuvent également recevoir par exemple le contenu du fichier ou ses informations de propriétaire ou ses privilèges ou tout autre objet différent d'un dict. Sauf si c'est un dictionnaire ça peut l'être facilement distingué d'un "type de répertoire" dans d'autres opérations (par exemple dans os_walk_mock ci-dessous).

Avoir un tel arbre dans un système de fichiers:

# bash:
$ tree /tmp/ex
/tmp/ex
├── d_a
│   ├── d_a_a
│   ├── d_a_b
│   │   └── f1.txt
│   ├── d_a_c
│   └── fa.txt
├── d_b
│   ├── fb1.txt
│   └── fb2.txt
└── d_c

, Le résultat sera:

# python 2 or 3:
>>> my_nice_tree = fs_tree_to_dict("/tmp/ex")
>>> my_nice_tree
{
    'd_a': {
        'd_a_a': {},
        'd_a_b': {
            'f1.txt': ''
        },
        'd_a_c': {},
        'fa.txt': ''
    },
    'd_b': {
        'fb1.txt': '',
        'fb2.txt': ''
    },
    'd_c': {}
}

J'ai écrit la fonction d'aide pour créer une fausse structure fs pour les tests unitaires (en s'appuyant sur real fs) de manière beaucoup plus élégante qu'une sortie factice de os.walk. L'utilisation des littéraux dict de python convient parfaitement ici.

Une telle "méthode" peut être utilisée pour simuler os.walk avec certains arbres comme argument:

def create_os_walk_mock(whole_tree):
    "return a callable with same interface as os.walk()"
    def actual_os_walk_interface(path_arg):
        def walk_mock(tree, path_=""):
            "the recursive flattening iterator"
            dirs = {n: v for n, v in tree.items() if isinstance(v, dict)}
            files = [n for n, v in tree.items() if not isinstance(v, dict)]

            yield path_, dirs.keys(), files
            for dir_name, dir_tree in dirs.items():
                for result in walk_mock(dir_tree, os.path.join(path_, dir_name)):
                    yield result
        return walk_mock(whole_tree, path_arg)
    return actual_os_walk_interface

os_walk_mock = create_os_walk_mock(my_nice_tree)

list(os_walk_mock("/tmp/ex"))
>>> [
    ('/tmp/ex', ['d_b', 'd_c', 'd_a'], []),
    ('/tmp/ex/d_b', [], ['fb1.txt', 'fb2.txt']),
    ('/tmp/ex/d_c', [], []),
    ('/tmp/ex/d_a', ['d_a_a', 'd_a_b', 'd_a_c'], ['fa.txt']),
    ('/tmp/ex/d_a/d_a_a', [], []),
    ('/tmp/ex/d_a/d_a_b', [], ['f1.txt']),
    ('/tmp/ex/d_a/d_a_c', [], []),
]

Qui donne exactement le même résultat que le résultat réel de list(os.walk("/tmp/ex")) (en négligeant l'ordre des éléments, qui est toujours incertain en traversant le réel fs).

1
répondu Mikaelblomkvistsson 2018-08-23 20:43:20