List directory tree structure in python?
我知道我们可以使用os.walk()列出一个目录中的所有子目录或所有文件。但是,我想列出完整的目录树内容:
- 子目录1:
- 文件11
- 文件12
- 子目录11:
- 文件111
- 文件112
- 子目录2:
- 文件21
- 子目录21
- 子目录22
- 子子目录221
- 文件2211
- 子子目录221
如何在Python中最好地实现这一点?
下面是一个函数,用于格式化:
1 2 3 4 5 6 7 8 9 10 | 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 2 3 4 | for path, dirs, files in os.walk(given_path): print path for f in files: print f |
os.walk已经完成了自上而下的深度优先行走。
忽略dirs列表可防止您提到的重叠。
我来这里是为了找同样的东西,我用了DHOBS的答案。作为感谢社区的一种方式,我添加了一些参数来写入一个文件,正如Akshay所要求的那样,并使显示文件成为可选的,因此它不是一个输出。还使缩进成为一个可选参数,这样您就可以更改它,因为有些人喜欢它是2,而另一些人喜欢它是4。
使用了不同的循环,因此不显示文件的循环不会在每次迭代中检查是否必须这样做。
希望它能帮助其他人,因为dhobbs的回答帮助了我。谢谢。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | 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(' ') else: # Default behaviour: print on screen. for line in tree: print line |
与上面的答案类似,但是对于python3,可以说是可读的,可以说是可扩展的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | 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)) |
示例用法:
1 2 3 | paths = DisplayablePath.make_tree(Path('doc')) for path in paths: print(path.displayable()) |
实例输出:
1 2 3 4 5 6 7 8 9 10 11 12 | doc/ ├── _static/ │ ├── embedded/ │ │ ├── deep_file │ │ └── very/ │ │ └── deep/ │ │ └── folder/ │ │ └── very_deep_file │ └── less_deep_file ├── about.rst ├── conf.py └── index.rst |
笔记
- 这使用递归。它将在非常深的文件夹树上引发递归错误
- 这棵树评价得很慢。它应该在非常宽的文件夹树上表现良好。但是,给定文件夹的直接子级不会被延迟计算。
编辑:
- 增加奖金!筛选路径的条件回调。
基于这篇精彩的文章
http://code.activestate.com/recipes/217212-treepy-graphically-displays-the-directory-structur/
这里有一种优雅的举止
http://linux.die.net/man/1/tree/树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | #!/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() |
1 2 3 4 5 6 7 8 | 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 |
如果有人感兴趣,那么递归函数返回字典的嵌套结构。键是(目录和文件的)
- 目录的子词典
- 文件字符串(见
file_token )
在此示例中,指定文件的字符串为空。它们也可以是给定的文件内容或其所有者信息或特权,或者是与dict不同的任何对象。除非它是字典,否则在进一步的操作中可以很容易地与"目录类型"区分开来。
在文件系统中有这样一棵树:
1 2 3 4 5 6 7 8 9 10 11 12 13 | # 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 |
结果将是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # 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': {} } |
如果您喜欢的话,我已经创建了一个包(python 2&3)和这个包(以及一个不错的
您可以执行Linuxshell的"tree"命令。
安装:
1 | ~$sudo apt install tree |
在Python中使用
1 2 | >>> import os >>> os.system('tree <desired path>') |
例子:
1 | >>> os.system('tree ~/Desktop/myproject') |
这给了你一个更干净的结构,视觉上更全面,更容易打字。
只有在系统上安装了
您可以告诉tree将树结构输出为xml(
以下面的目录结构为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | [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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | <?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> |
杰森
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | [sri@localhost projects]$tree-j波段["type":"directory","name":"bands","contents":。["type":"directory","name":"dreamtroll","contents":。["type":"file","name":"mattbaldwinson<hr><P>也许比@ellockie更快(也许)</P>[cc lang="python"] 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 = '{}{}/ '.format(indent, os.path.basename(root)) file_writer(output_string) subindent = '\t' * 1 * (level + 1) output_string = '%s %s ' %(subindent,[f for f in files]) file_writer(''.join(output_string)) list_files("/") |
以下屏幕截图中的测试结果:
除了上面的dhobbs答案(https://stackoverflow.com/a/9728478/624597),这里还有一个将结果存储到文件中的额外功能(我个人使用它来复制和粘贴到freemind,以便对结构有一个很好的概述,因此我使用了制表符而不是缩进的空格):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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 + ' ') subindent = '\t' * 1 * (level + 1) for f in files: output_string = '{}{}'.format(subindent, f) print(output_string) f_output.write(output_string + ' ') list_files(".") |