标签归档:traversal

在python中列出目录树结构?

问题:在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中最好地实现这一目标?

I know that we can use os.walk() to list all sub-directories or all files in a directory. However, I would like to list the full directory tree content:

- 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

How to best achieve this in 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))

Here’s a function to do that with formatting:

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
  • 懒惰地评估树。它在非常宽的文件夹树上应该表现良好。但是,给定文件夹的直系子代不会被延迟计算。

编辑:

  • 增加了奖励!用于过滤路径的条件回调。

Similar to answers above, but for python3, arguably readable and arguably 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))

Example usage:

paths = DisplayablePath.make_tree(Path('doc'))
for path in paths:
    print(path.displayable())

Example output:

doc/
├── _static/
│   ├── embedded/
│   │   ├── deep_file
│   │   └── very/
│   │       └── deep/
│   │           └── folder/
│   │               └── very_deep_file
│   └── less_deep_file
├── about.rst
├── conf.py
└── index.rst

Notes

  • This uses recursion. It will raise a RecursionError on really deep folder trees
  • The tree is lazily evaluated. It should behave well on really wide folder trees. Immediate children of a given folder are not lazily evaluated, though.

Edit:

  • Added bonus! criteria callback for filtering paths.

回答 2

没有缩进的解决方案:

for path, dirs, files in os.walk(given_path):
  print path
  for f in files:
    print f

os.walk已经完成了您要寻找的自上而下,深度优先的步行。

忽略Dirs列表可避免您提到的重叠。

A solution without your indentation:

for path, dirs, files in os.walk(given_path):
  print path
  for f in files:
    print f

os.walk already does the top-down, depth-first walk you are looking for.

Ignoring the dirs list prevents the overlapping you mention.


回答 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对象
  • 使用yieldand 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
        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而不是具体化目录内容列表,但这可能会有负面的折衷,并且可能会使代码变得更加复杂。

欢迎发表评论!

List directory tree structure in Python?

We usually prefer to just use GNU tree, but we don’t always have tree on every system, and sometimes Python 3 is available. A good answer here could be easily copy-pasted and not make GNU tree a requirement.

tree‘s output looks like this:

$ 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

I created the above directory structure in my home directory under a directory I call pyscratch.

I also see other answers here that approach that sort of output, but I think we can do better, with simpler, more modern code and lazily evaluating approaches.

Tree in Python

To begin with, let’s use an example that

  • uses the Python 3 Path object
  • uses the yield and yield from expressions (that create a generator function)
  • uses recursion for elegant simplicity
  • uses comments and some type annotations for extra clarity
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)

and now:

for line in tree(Path.home() / 'pyscratch'):
    print(line)

prints:

├── package
│   ├── __init__.py
│   ├── __main__.py
│   ├── subpackage
│   │   ├── __init__.py
│   │   ├── __main__.py
│   │   └── module.py
│   └── subpackage2
│       ├── __init__.py
│       ├── __main__.py
│       └── module2.py
└── package2
    └── __init__.py

We do need to materialize each directory into a list because we need to know how long it is, but afterwards we throw the list away. For deep and broad recursion this should be lazy enough.

The above code, with the comments, should be sufficient to fully understand what we’re doing here, but feel free to step through it with a debugger to better grock it if you need to.

More features

Now GNU tree gives us a couple of useful features that I’d like to have with this function:

  • prints the subject directory name first (does so automatically, ours does not)
  • prints the count of n directories, m files
  • option to limit recursion, -L level
  • option to limit to just directories, -d

Also, when there is a huge tree, it is useful to limit the iteration (e.g. with islice) to avoid locking up your interpreter with text, as at some point the output becomes too verbose to be useful. We can make this arbitrarily high by default – say 1000.

So let’s remove the previous comments and fill out this functionality:

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 ''))

And now we can get the same sort of output as tree:

tree(Path.home() / 'pyscratch')

prints:

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

And we can restrict to levels:

tree(Path.home() / 'pyscratch', level=2)

prints:

pyscratch
├── package
│   ├── __init__.py
│   ├── __main__.py
│   ├── subpackage
│   └── subpackage2
└── package2
    └── __init__.py

4 directories, 3 files

And we can limit the output to directories:

tree(Path.home() / 'pyscratch', level=2, limit_to_directories=True)

prints:

pyscratch
├── package
│   ├── subpackage
│   └── subpackage2
└── package2

4 directories

Retrospective

In retrospect, we could have used path.glob for matching. We could also perhaps use path.rglob for recursive globbing, but that would require a rewrite. We could also use itertools.tee instead of materializing a list of directory contents, but that could have negative tradeoffs and would probably make the code even more complex.

Comments are welcome!


回答 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

I came here looking for the same thing and used dhobbs answer for me. As a way of thanking the community, I added some arguments to write to a file, as akshay asked, and made showing files optional so it is not so bit an output. Also made the indentation an optional argument so you can change it, as some like it to be 2 and others prefer 4.

Used different loops so the one not showing files doesn’t check if it has to on each iteration.

Hope it helps someone else as dhobbs answer helped me. Thanks a lot.

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 () 

Based on this fantastic post

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

Here es a refinement to behave exactly like

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()



回答 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/

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

If anybody is interested – that recursive function returns nested structure of dictionaries. Keys are file system names (of directories and files), values are either:

  • sub dictionaries for directories
  • strings for files (see file_token)

The strings designating files are empty in this example. They can also be e.g. given file contents or its owner info or privileges or whatever object different than a dict. Unless it’s a dictionary it can be easily distinguished from a “directory type” in further operations.

Having such a tree in a filesystem:

# 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

The result will be:

# 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': {}
}

If you like that, I’ve already created a package (python 2 & 3) with this stuff (and a nice pyfakefs helper): 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(".")

On top of dhobbs answer above (https://stackoverflow.com/a/9728478/624597), here is an extra functionality of storing results to a file (I personally use it to copy and paste to FreeMind to have a nice overview of the structure, therefore I used tabs instead of spaces for 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(".")

回答 8

您可以执行Linux Shell的“ tree”命令。

安装:

   ~$sudo apt install tree

在python中使用

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

例:

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

这样可以使您的结构更整洁,并且在外观上更全面,更易于键入。

You can execute ‘tree’ command of Linux shell.

Installation:

   ~$sudo apt install tree

Using in python

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

Example:

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

This gives you a cleaner structure and is visually more comprehensive and easy to type.


回答 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}
]

This solution will only work if you have tree installed on your system. However I’m leaving this solution here just in case it helps someone else out.

You can tell tree to output the tree structure as XML (tree -X) or JSON (tree -J). JSON of course can be parsed directly with python and XML can easily be read with lxml.

With the following directory structure as an example:

[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(“ /”)

测试结果在以下屏幕截图中:

Maybe faster than @ellockie ( Maybe )

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("/")

Test results in screenshot below:


回答 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

Here you can find code with output like this: 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}

For those who are still looking for an answer. Here is a recursive approach to get the paths in a dictionary.

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
================================================================

您可以通过|计数获得级别!

@dhobbs’s answer is great!

but simply change to easy get the level info

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)

and the output like

================================================================
[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
================================================================

you can get the level by | count!