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
- sous-sous-sous-sous-répertoire 221
Comment y parvenir au mieux en Python?
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))
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.
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
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()
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.
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.
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(".")
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:
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
).