我知道我们可以使用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 :(得分:100)
以下是使用格式化执行此操作的功能:
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 :(得分:18)
没有缩进的解决方案:
for path, dirs, files in os.walk(given_path):
print path
for f in files:
print f
os.walk已经完成了你正在寻找的自上而下,深度优先的步行。
忽略dirs列表可防止您提及的重叠。
答案 2 :(得分:9)
我来这里寻找同样的事情,并为我使用了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
答案 3 :(得分:6)
基于这个精彩的帖子
http://code.activestate.com/recipes/217212-treepy-graphically-displays-the-directory-structur/
这里的细化表现与
完全相同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 :(得分:6)
与上面的答案类似,但对于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
答案 5 :(得分:5)
使用Python列出目录树结构吗?
我们通常更喜欢只使用GNU树,但是我们并不总是在每个系统上都使用tree
,有时Python 3可用。一个很好的答案很容易复制粘贴,而不需要GNU tree
。
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
的目录下的主目录中创建了以上目录结构。
在这里我还看到了其他类似的答案,但我认为我们可以通过使用更简单,更现代的代码并懒惰地评估这些方法来做得更好。
首先,让我们使用一个示例
Path
对象yield
和yield 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
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
而不是具体化目录内容列表,但这可能会带来负面的折衷,并且可能会使代码变得更加复杂。
欢迎评论!
答案 6 :(得分:4)
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 :(得分:2)
您可以执行Linux shell的'tree'命令。
安装:
~$sudo apt install tree
在python中使用
>>> import os
>>> os.system('tree <desired path>')
示例:
>>> os.system('tree ~/Desktop/myproject')
这为您提供了更清晰的结构,视觉上更加全面,易于打字。
答案 8 :(得分:1)
除了上面的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(".")
答案 9 :(得分:1)
可能比@ellockie(可能)更快
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("/")
以下屏幕截图中的测试结果:
答案 10 :(得分:1)
仅当系统上安装了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}
]
答案 11 :(得分:1)
有一个名为seedir
的软件包(我创建),用于通过文件夹树形图进行此操作和其他操作:
>>> import seedir as sd
>>> sd.seedir('/path/to/some/path/or/package', style='emoji')
? package/
├─? __init__.py
├─? subpackage1/
│ ├─? __init__.py
│ ├─? moduleX.py
│ └─? moduleY.py
├─? subpackage2/
│ ├─? __init__.py
│ └─? moduleZ.py
└─? moduleA.py
答案 12 :(得分:0)
在这里您可以找到具有以下输出的代码: 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
答案 13 :(得分:0)
对于那些仍在寻找答案的人。这是一种在字典中获取路径的递归方法。
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}
答案 14 :(得分:0)
但只需更改即可轻松获得关卡信息
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
================================================================
您可以通过|
个计数来获得等级!