Структура дерева каталогов списка в python?

Я знаю, что мы можем использовать ОС.walk (), чтобы перечислить все подкаталоги или все файлы в каталоге. Тем не менее, я хотел бы перечислить полное содержимое дерева каталогов:

  • поддиректорию 1:
    • file11
    • file12
    • суб-суб-директорию 11:
      • file111
      • file112
  • поддиректорию 2:
    • file21
    • суб-суб-директории 21
    • суб-суб-каталог 22
      • sub-sub-sub-directory 221
          2211

Как лучше всего достичь этого в Python?

9 ответов


вот функция для форматирования:

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

решение без отступа:

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

os.walk уже делает нисходящую, глубокую первую прогулку, которую вы ищете.

игнорирование списка dirs предотвращает перекрытие, которое вы упомянули.


Я пришел сюда, ища то же самое, и использовал ответ dhobbs для меня. Чтобы поблагодарить сообщество, я добавил несколько аргументов для записи в файл, как просил Акшай, и сделал показ файлов необязательным, чтобы это не было таким битным выходом. Также сделал отступ необязательным аргументом, чтобы вы могли его изменить, так как некоторые любят 2, а другие предпочитают 4.

используются разные циклы, поэтому тот, который не показывает файлы, не проверяет, должен ли он на каждой итерации.

надеюсь, это поможет кто-то еще, как ответил дхоббс, помог мне. Большое спасибо.

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

на основе этого фантастического должности

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



вы можете выполнить команду "дерево" оболочки Linux.

установка:

   ~$sudo apt install tree

использование в python

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

пример:

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

Это дает вам более чистую структуру и визуально более всесторонний и легкий для того чтобы напечатать.


подобно ответам выше, но для 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 на самом деле глубокий папка деревьев
  • дерево лениво оценены. Он должен хорошо себя вести на самом деле широкое папка деревьев. Непосредственные дети из данной папки не лениво оцениваются, хотя.

Edit:

  • дополнительный бонус! критерий обратного вызова для путей фильтрации.

поверх ответа 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(".")

может быть, быстрее, чем @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("/")

Edit: я был протестирован скриншот таков:

enter image description here


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 имена (каталогов и файлов), значения:

  • sub словари для каталогов
  • строки для файлов (см. file_token)

в этом примере строки, обозначающие файлы, пусты. Они также могут быть, например, содержимое файла или его владелец информация или привилегии или любой объект, отличный от dict. Если это не словарь, его можно легко отличить от "типа каталога" в дальнейших операциях (например, в os_walk_mock ниже).

наличие такого дерева в файловой системе:

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

я написал вспомогательную функцию для создания подделки fs структурное модульное тестирование fot (на основе real fs) С гораздо более элегантным способом, чем фиктивный вывод os.walk. С помощью языка Python dict литералы отлично подходят здесь.

такой "метод" можно использовать для издевательства os.walk С ОПРЕДЕЛЕННЫМ деревом в качестве аргумента:

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', [], []),
]

который дает точно такой же результат, как и реальный результат list(os.walk("/tmp/ex")) (пренебрегая порядком элементов, который всегда неопределенен при прохождении реального fs).