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 ?

10voto

albfan Points 2333

Sur la base de cet article fantastique

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

Voici un raffinement qui permet de 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()

9voto

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 cela intéresse quelqu'un, cette fonction récursive renvoie une structure imbriquée de dictionnaires. Les clés sont file system noms (de répertoires et de fichiers), les valeurs sont soit :

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

Les chaînes désignant les fichiers sont vides dans cet exemple. On peut aussi leur donner par exemple le contenu d'un fichier, les informations sur son propriétaire, ses privilèges ou tout autre objet différent d'un dict. À moins qu'il ne s'agisse d'un dictionnaire, il peut être facilement distingué d'un "type de répertoire" dans les opérations ultérieures.

L'existence d'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 le suivant :

# python 2 or 3:
>>> fs_tree_to_dict("/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': {}
}

Si vous aimez cela, j'ai déjà créé un package (python 2 & 3) avec ce matériel (et un joli pyfakefs ) : https://pypi.org/project/fsforge/

0 votes

Pourriez-vous nous expliquer comment file_token pourrait être utilisé pour lire les propriétés d'un fichier, comme par exemple filesize , merci !

0 votes

Bien sûr. Modifier la ligne tree.update({f: file_token for f in files}) en tree.update({f: os.path.getsize(os.path.join(root, f)) for f in files}) . La ligne définissant file_token peut alors être supprimée.

5voto

ellockie Points 928

En plus de la réponse de Dhobbs ci-dessus ( https://stackoverflow.com/a/9728478/624597 ), voici une fonctionnalité supplémentaire qui permet de stocker les résultats dans un fichier (je l'utilise personnellement pour copier et coller vers FreeMind pour avoir une bonne vue d'ensemble de la structure, c'est pourquoi j'ai utilisé des tabulations 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(".")

4voto

shrewmouse Points 1683

Cette solution ne fonctionnera que si vous avez tree installé sur votre système. Cependant, je laisse cette solution ici au cas où elle aiderait quelqu'un d'autre.

Vous pouvez demander à tree de produire la structure de l'arbre au format XML ( tree -X ) ou JSON ( tree -J ). JSON peut bien sûr être analysé directement avec python et XML peut facilement être lu avec lxml .

Prenons l'exemple de la structure de répertoire suivante :

[sri@localhost Projects]$ tree --charset=ascii bands
bands
|-- DreamTroll
|   |-- MattBaldwinson
|   |-- members.txt
|   |-- PaulCarter
|   |-- SimonBlakelock
|   `-- Rob Stringer
|-- KingsX
|   |-- DougPinnick
|   |-- JerryGaskill
|   |-- members.txt
|   `-- TyTabor
|-- Megadeth
|   |-- DaveMustaine
|   |-- DavidEllefson
|   |-- DirkVerbeuren
|   |-- KikoLoureiro
|   `-- members.txt
|-- Nightwish
|   |-- EmppuVuorinen
|   |-- FloorJansen
|   |-- JukkaNevalainen
|   |-- MarcoHietala
|   |-- members.txt
|   |-- TroyDonockley
|   `-- TuomasHolopainen
`-- Rush
    |-- AlexLifeson
    |-- GeddyLee
    `-- NeilPeart

5 directories, 25 files

XML

<?xml version="1.0" encoding="UTF-8"?>
<tree>
  <directory name="bands">
    <directory name="DreamTroll">
      <file name="MattBaldwinson"></file>
      <file name="members.txt"></file>
      <file name="PaulCarter"></file>
      <file name="RobStringer"></file>
      <file name="SimonBlakelock"></file>
    </directory>
    <directory name="KingsX">
      <file name="DougPinnick"></file>
      <file name="JerryGaskill"></file>
      <file name="members.txt"></file>
      <file name="TyTabor"></file>
    </directory>
    <directory name="Megadeth">
      <file name="DaveMustaine"></file>
      <file name="DavidEllefson"></file>
      <file name="DirkVerbeuren"></file>
      <file name="KikoLoureiro"></file>
      <file name="members.txt"></file>
    </directory>
    <directory name="Nightwish">
      <file name="EmppuVuorinen"></file>
      <file name="FloorJansen"></file>
      <file name="JukkaNevalainen"></file>
      <file name="MarcoHietala"></file>
      <file name="members.txt"></file>
      <file name="TroyDonockley"></file>
      <file name="TuomasHolopainen"></file>
    </directory>
    <directory name="Rush">
      <file name="AlexLifeson"></file>
      <file name="GeddyLee"></file>
      <file name="NeilPeart"></file>
    </directory>
  </directory>
  <report>
    <directories>5</directories>
    <files>25</files>
  </report>
</tree>

JSON

[sri@localhost Projects]$ tree -J bands
[
  {"type":"directory","name":"bands","contents":[
    {"type":"directory","name":"DreamTroll","contents":[
      {"type":"file","name":"MattBaldwinson"},
      {"type":"file","name":"members.txt"},
      {"type":"file","name":"PaulCarter"},
      {"type":"file","name":"RobStringer"},
      {"type":"file","name":"SimonBlakelock"}
    ]},
    {"type":"directory","name":"KingsX","contents":[
      {"type":"file","name":"DougPinnick"},
      {"type":"file","name":"JerryGaskill"},
      {"type":"file","name":"members.txt"},
      {"type":"file","name":"TyTabor"}
    ]},
    {"type":"directory","name":"Megadeth","contents":[
      {"type":"file","name":"DaveMustaine"},
      {"type":"file","name":"DavidEllefson"},
      {"type":"file","name":"DirkVerbeuren"},
      {"type":"file","name":"KikoLoureiro"},
      {"type":"file","name":"members.txt"}
    ]},
    {"type":"directory","name":"Nightwish","contents":[
      {"type":"file","name":"EmppuVuorinen"},
      {"type":"file","name":"FloorJansen"},
      {"type":"file","name":"JukkaNevalainen"},
      {"type":"file","name":"MarcoHietala"},
      {"type":"file","name":"members.txt"},
      {"type":"file","name":"TroyDonockley"},
      {"type":"file","name":"TuomasHolopainen"}
    ]},
    {"type":"directory","name":"Rush","contents":[
      {"type":"file","name":"AlexLifeson"},
      {"type":"file","name":"GeddyLee"},
      {"type":"file","name":"NeilPeart"}
    ]}
  ]},
  {"type":"report","directories":5,"files":25}
]

2voto

Vous pouvez exécuter la commande 'tree' de l'interpréteur de commandes Linux.

Installation :

   ~$sudo apt install tree

Utilisation en python

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

Exemple :

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

Vous obtenez ainsi une structure plus propre, visuellement plus complète et plus facile à taper.

0 votes

Cette solution n'est pas très portable car elle ne fonctionne pas sous Windows et dépend d'un programme supplémentaire.

0 votes

@oglop, tree est une commande dos standard. fr.wikipedia.org/wiki/List_of_DOS_commands#TREE

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