问题:在python中列出目录树结构?
我知道我们可以os.walk()
用来列出目录中的所有子目录或所有文件。但是,我想列出完整的目录树内容:
- 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
如何在Python中最好地实现这一目标?
回答 0
这是一个用于格式化的函数:
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
与上述答案类似,但对于python3来说,可以说是可读性和可扩展性的:
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))
用法示例:
paths = DisplayablePath.make_tree(Path('doc'))
for path in paths:
print(path.displayable())
输出示例:
doc/
├── _static/
│ ├── embedded/
│ │ ├── deep_file
│ │ └── very/
│ │ └── deep/
│ │ └── folder/
│ │ └── very_deep_file
│ └── less_deep_file
├── about.rst
├── conf.py
└── index.rst
笔记
- 这使用递归。它将在非常深的文件夹树上引发RecursionError
- 懒惰地评估树。它在非常宽的文件夹树上应该表现良好。但是,给定文件夹的直系子代不会被延迟计算。
编辑:
- 增加了奖励!用于过滤路径的条件回调。
回答 2
没有缩进的解决方案:
for path, dirs, files in os.walk(given_path):
print path
for f in files:
print f
os.walk已经完成了您要寻找的自上而下,深度优先的步行。
忽略Dirs列表可避免您提到的重叠。
回答 3
列出Python中的目录树结构?
我们通常更喜欢只使用GNU树,但是我们并不总是tree
在每个系统上都有,有时Python 3可用。一个很好的答案很容易复制粘贴,而不是tree
要求GNU 。
tree
的输出如下所示:
$ tree
.
├── package
│ ├── __init__.py
│ ├── __main__.py
│ ├── subpackage
│ │ ├── __init__.py
│ │ ├── __main__.py
│ │ └── module.py
│ └── subpackage2
│ ├── __init__.py
│ ├── __main__.py
│ └── module2.py
└── package2
└── __init__.py
4 directories, 9 files
我在我的主目录下创建了上述目录结构pyscratch
。
我在这里还看到了其他类似的答案,可以实现这种输出,但是我认为我们可以通过使用更简单,更现代的代码并懒惰地评估这些方法来做得更好。
Python中的树
首先,让我们使用一个示例
- 使用Python 3
Path
对象 - 使用
yield
andyield from
表达式(创建一个生成器函数) - 使用递归实现优雅的简洁性
- 使用注释和一些类型注释以更加清晰
from pathlib import Path
# prefix components:
space = ' '
branch = '│ '
# pointers:
tee = '├── '
last = '└── '
def tree(dir_path: Path, prefix: str=''):
"""A recursive generator, given a directory Path object
will yield a visual tree structure line by line
with each line prefixed by the same characters
"""
contents = list(dir_path.iterdir())
# contents each get pointers that are ├── with a final └── :
pointers = [tee] * (len(contents) - 1) + [last]
for pointer, path in zip(pointers, contents):
yield prefix + pointer + path.name
if path.is_dir(): # extend the prefix and recurse:
extension = branch if pointer == tee else space
# i.e. space because last, └── , above so no more |
yield from tree(path, prefix=prefix+extension)
现在:
for line in tree(Path.home() / 'pyscratch'):
print(line)
印刷品:
├── package
│ ├── __init__.py
│ ├── __main__.py
│ ├── subpackage
│ │ ├── __init__.py
│ │ ├── __main__.py
│ │ └── module.py
│ └── subpackage2
│ ├── __init__.py
│ ├── __main__.py
│ └── module2.py
└── package2
└── __init__.py
我们确实需要将每个目录具体化为一个列表,因为我们需要知道目录的长度,但是之后我们将列表丢弃。对于深度和广泛的递归,这应该足够懒惰。
上面的代码,加上注释,应该足以完全理解我们在这里所做的事情,但是如果需要,可以随时使用调试器逐步调试它,以便更好地了解它。
更多功能
现在,GNU tree
为我们提供了一些我想使用此功能的有用功能:
- 首先打印主题目录名称(自动打印,我们不打印)
- 打印计数
n directories, m files
- 限制递归的选项,
-L level
- 仅限于目录的选项,
-d
此外,当有一棵大树时,限制迭代次数(例如使用islice
)是有用的,以避免用文本锁定您的解释器,因为有时输出变得太冗长而无用。我们可以默认将其设置为任意高-例如1000
。
因此,让我们删除先前的注释并填写此功能:
from pathlib import Path
from itertools import islice
space = ' '
branch = '│ '
tee = '├── '
last = '└── '
def tree(dir_path: Path, level: int=-1, limit_to_directories: bool=False,
length_limit: int=1000):
"""Given a directory Path object print a visual tree structure"""
dir_path = Path(dir_path) # accept string coerceable to Path
files = 0
directories = 0
def inner(dir_path: Path, prefix: str='', level=-1):
nonlocal files, directories
if not level:
return # 0, stop iterating
if limit_to_directories:
contents = [d for d in dir_path.iterdir() if d.is_dir()]
else:
contents = list(dir_path.iterdir())
pointers = [tee] * (len(contents) - 1) + [last]
for pointer, path in zip(pointers, contents):
if path.is_dir():
yield prefix + pointer + path.name
directories += 1
extension = branch if pointer == tee else space
yield from inner(path, prefix=prefix+extension, level=level-1)
elif not limit_to_directories:
yield prefix + pointer + path.name
files += 1
print(dir_path.name)
iterator = inner(dir_path, level=level)
for line in islice(iterator, length_limit):
print(line)
if next(iterator, None):
print(f'... length_limit, {length_limit}, reached, counted:')
print(f'\n{directories} directories' + (f', {files} files' if files else ''))
现在我们可以得到与以下相同的输出tree
:
tree(Path.home() / 'pyscratch')
印刷品:
pyscratch
├── package
│ ├── __init__.py
│ ├── __main__.py
│ ├── subpackage
│ │ ├── __init__.py
│ │ ├── __main__.py
│ │ └── module.py
│ └── subpackage2
│ ├── __init__.py
│ ├── __main__.py
│ └── module2.py
└── package2
└── __init__.py
4 directories, 9 files
我们可以限制级别:
tree(Path.home() / 'pyscratch', level=2)
印刷品:
pyscratch
├── package
│ ├── __init__.py
│ ├── __main__.py
│ ├── subpackage
│ └── subpackage2
└── package2
└── __init__.py
4 directories, 3 files
我们可以将输出限制为目录:
tree(Path.home() / 'pyscratch', level=2, limit_to_directories=True)
印刷品:
pyscratch
├── package
│ ├── subpackage
│ └── subpackage2
└── package2
4 directories
回顾性
回想起来,我们本可以用于path.glob
匹配。我们也许还可以path.rglob
用于递归遍历,但这将需要重写。我们也可以使用itertools.tee
而不是具体化目录内容列表,但这可能会有负面的折衷,并且可能会使代码变得更加复杂。
欢迎发表评论!
回答 4
我来这里寻找相同的东西,并用dhobbs回答我。作为感谢社区的一种方式,按照akshay的要求,我添加了一些参数来写入文件,并使显示文件成为可选文件,因此它并不是输出。还使缩进成为可选参数,以便您可以更改它,例如有些人喜欢将其设为2,而另一些人喜欢采用4。
使用了不同的循环,因此不显示文件的循环不会在每次迭代中检查是否必须这样做。
希望它能对其他人有所帮助,因为dhobbs的回答对我有所帮助。非常感谢。
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
回答 5
基于这个很棒的帖子
http://code.activestate.com/recipes/217212-treepy-graphically-displays-the-directory-structur/
这是一种行为,完全像
http://linux.die.net/man/1/tree
#!/ usr / bin / env python2 #-*-编码:utf-8-*- #tree.py ##作者:Doug DAHMS ##打印的树结构的命令行上指定的路径 从OS 进口listdir同时,九月 的操作系统。路径导入abspath ,basename ,来自sys import argv的isdir def 树(dir ,padding ,print_files = False ,isLast = False ,isFirst = False ):如果isFirst :打印填充。解码('utf8' )[:- 1 ]。编码('utf8' )+ 目录 其他:如果isLast :打印填充。解码('utf8' )[:- 1 ]。 编码('utf8' )+ '└──' + 基本名称(abspath (dir ))else :打印填充。解码('utf8' )[:- 1 ]。编码('utf8' )+ '├──' + 基本名称(abspath (dir )) 文件= [],如果print_files : 文件= listdir (dir )否则: 文件= [ X 为X 在listdir同时(DIR )如果ISDIR (DIR + 月+ X )] 如果不isFirst : 填充= 填充+ '' 文件= 排序(文件,键= 拉姆达小号:小号。降低()) 计数= 0 最后= len ( 文件)- 1 为我,文件在枚举(文件): 计数+ = 1 路径= DIR + 月+ 文件 isLast = i == 最后一次, 如果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 + '\'不是目录' else :打印用法() 如果__name__ == ' __main__ ' : main ()
回答 6
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 system
(目录和文件的)名称,值是:
- 目录子词典
- 文件字符串(请参阅
file_token
)
在此示例中,指定文件的字符串为空。例如,还可以为它们提供文件内容,其所有者信息或特权或与dict不同的任何对象。除非它是字典,否则在后续操作中可以很容易地将其与“目录类型”区分开。
在文件系统中具有这样的树:
# 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
结果将是:
# 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': {}
}
如果您愿意,我已经用这个东西(和一个很好的pyfakefs
助手)创建了一个包(python 2和3 ):https :
//pypi.org/project/fsforge/
回答 7
除了上面的dhobbs答案(https://stackoverflow.com/a/9728478/624597)之外,这里还有将结果存储到文件的额外功能(我个人使用它来复制并粘贴到FreeMind上,以对结构,因此我使用制表符而不是空格进行缩进):
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(".")
回答 8
您可以执行Linux Shell的“ tree”命令。
安装:
~$sudo apt install tree
在python中使用
>>> import os
>>> os.system('tree <desired path>')
例:
>>> os.system('tree ~/Desktop/myproject')
这样可以使您的结构更整洁,并且在外观上更全面,更易于键入。
回答 9
仅当您已tree
在系统上安装此解决方案。但是,我将其保留在此处,以防万一它可以帮助其他人。
您可以告诉tree将树结构输出为XML(tree -X
)或JSON(tree -J
)。JSON当然可以直接用python解析,而XML可以轻松地使用读取lxml
。
以以下目录结构为例:
[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}
]
回答 10
也许比@ellockie(也许)快
导入操作系统 def file_writer(文字): 使用open(“ folder_structure.txt”,“ a”)作为f_output: f_output.write(文本) def list_files(起始路径): 用于os.walk(startpath)中的根目录,dirs文件: 级别= root.replace(startpath,'').count(os.sep) 缩进='\ t'* 1 *(级别) output_string ='{} {} / \ n'.format(indent,os.path.basename(root)) file_writer(输出字符串) subindent ='\ t'* 1 *(级别+1) output_string ='%s%s \ n'%(subindent,[f用于文件中的f]) file_writer(''。join(output_string)) list_files(“ /”)
测试结果在以下屏幕截图中:
回答 11
在这里您可以找到具有以下输出的代码:https : //stackoverflow.com/a/56622847/6671330
V .
|-> V folder1
| |-> V folder2
| | |-> V folder3
| | | |-> file3.txt
| | |-> file2.txt
| |-> V folderX
| |-> file1.txt
|-> 02-hw1_wdwwfm.py
|-> 06-t1-home1.py
|-> 06-t1-home2.py
|-> hw1.py
回答 12
对于那些仍在寻找答案的人。这是一种获取字典中路径的递归方法。
import os
def list_files(startpath):
for root, dirs, files in os.walk(startpath):
dir_content = []
for dir in dirs:
go_inside = os.path.join(startpath, dir)
dir_content.append(list_files(go_inside))
files_lst = []
for f in files:
files_lst.append(f)
return {'name': root, 'files': files_lst, 'dirs': dir_content}
回答 13
@dhobbs的答案很棒!
但只需更改即可轻松获得关卡信息
def print_list_dir(dir):
print("=" * 64)
print("[PRINT LIST DIR] %s" % dir)
print("=" * 64)
for root, dirs, files in os.walk(dir):
level = root.replace(dir, '').count(os.sep)
indent = '| ' * level
print('{}{} \\'.format(indent, os.path.basename(root)))
subindent = '| ' * (level + 1)
for f in files:
print('{}{}'.format(subindent, f))
print("=" * 64)
和输出像
================================================================
[PRINT LIST DIR] ./
================================================================
\
| os_name.py
| json_loads.py
| linspace_python.py
| list_file.py
| to_gson_format.py
| type_convert_test.py
| in_and_replace_test.py
| online_log.py
| padding_and_clipping.py
| str_tuple.py
| set_test.py
| script_name.py
| word_count.py
| get14.py
| np_test2.py
================================================================
您可以通过|
计数获得级别!