파이썬에서 직접 하위 디렉토리를 모두 얻는 방법


150

모든 하위 디렉토리의 index.tpl을 index.html에 복사하는 간단한 Python 스크립트를 작성하려고합니다 (몇 가지 예외가 있음).

하위 디렉토리 목록을 가져 와서 혼란에 빠졌습니다.


11
:이 이전 SO 질문에서 허용 대답은 문제를 해결하는 것을 알 수 있습니다 stackoverflow.com/questions/120656/directory-listing-in-python
자렛 하디

답변:


31

모든 현재 하위 디렉토리 의 전체 경로 를 반환하기 위해 다양한 기능에 대한 속도 테스트수행 했습니다 .

tl; dr : 항상 사용 scandir:

list_subfolders_with_paths = [f.path for f in os.scandir(path) if f.is_dir()]

보너스 : scandir를 사용하는 f.name대신을 사용하여 폴더 이름 만 가져올 수도 있습니다 f.path.

이것 (및 아래의 다른 모든 기능)은 자연 정렬을 사용하지 않습니다 . 즉, 결과는 1, 10, 2로 정렬됩니다. 자연 정렬 (1, 2, 10)을 얻으려면 https://stackoverflow.com/a/48030307/2441026참조하십시오.




결과 : scandiris : 3 배 이상 walk, 32 배 이상 listdir(필터 포함), 35 배 이상 Pathlib, 36 배 이상 listdir, 37 배 (!) 빠릅니다 glob.

Scandir:           0.977
Walk:              3.011
Listdir (filter): 31.288
Pathlib:          34.075
Listdir:          35.501
Glob:             36.277

W7x64, Python 3.8.1로 테스트되었습니다. 440 개의 하위 폴더가있는 폴더 os.path.join ()을 두 번 수행하지 않고 속도를 높일 수
있는지 궁금한 경우 listdir에는 기본적으로 차이가 없습니다.

암호:

import os
import pathlib
import timeit
import glob

path = r"<example_path>"



def a():
    list_subfolders_with_paths = [f.path for f in os.scandir(path) if f.is_dir()]
    # print(len(list_subfolders_with_paths))


def b():
    list_subfolders_with_paths = [os.path.join(path, f) for f in os.listdir(path) if os.path.isdir(os.path.join(path, f))]
    # print(len(list_subfolders_with_paths))


def c():
    list_subfolders_with_paths = []
    for root, dirs, files in os.walk(path):
        for dir in dirs:
            list_subfolders_with_paths.append( os.path.join(root, dir) )
        break
    # print(len(list_subfolders_with_paths))


def d():
    list_subfolders_with_paths = glob.glob(path + '/*/')
    # print(len(list_subfolders_with_paths))


def e():
    list_subfolders_with_paths = list(filter(os.path.isdir, [os.path.join(path, f) for f in os.listdir(path)]))
    # print(len(list(list_subfolders_with_paths)))


def f():
    p = pathlib.Path(path)
    list_subfolders_with_paths = [x for x in p.iterdir() if x.is_dir()]
    # print(len(list_subfolders_with_paths))



print(f"Scandir:          {timeit.timeit(a, number=1000):.3f}")
print(f"Listdir:          {timeit.timeit(b, number=1000):.3f}")
print(f"Walk:             {timeit.timeit(c, number=1000):.3f}")
print(f"Glob:             {timeit.timeit(d, number=1000):.3f}")
print(f"Listdir (filter): {timeit.timeit(e, number=1000):.3f}")
print(f"Pathlib:          {timeit.timeit(f, number=1000):.3f}")

1
고마워하고 싶었고, 정말로 이것을 찾고있었습니다. 훌륭한 분석.
Cing

225
import os
def get_immediate_subdirectories(a_dir):
    return [name for name in os.listdir(a_dir)
            if os.path.isdir(os.path.join(a_dir, name))]

76

왜 아무도 언급하지 않았 glob습니까? glob유닉스 스타일의 경로 이름 확장을 사용할 수 있으며, 둘 이상의 경로 이름을 찾는 데 필요한 거의 모든 기능을 수행합니다. 매우 쉽습니다.

from glob import glob
paths = glob('*/')

glob(유닉스처럼) 최종 슬래시 디렉토리를 반환합니다 대부분의 동안path 기반 솔루션이 최종 슬래시를 생략합니다.


3
좋은 해결책, 간단하고 작동합니다. 마지막 슬래시를 원하지 않는 사람들은 이것을 사용할 수 있습니다 paths = [ p.replace('/', '') for p in glob('*/') ].
Evan Hu

5
[p[:-1] for p in paths]replace 메소드는 파일 이름에서 이스케이프 된 슬래시를 대체하기 때문에 마지막 문자를 간단히 잘라내는 것이 더 안전 할 수 있습니다 (공통이 아님).
ari November

3
더 안전하면 strip ( '/')을 사용하여 후행 슬래시를 제거하십시오. 이렇게하면 슬래시가 아닌 문자를 잘라 내지 않아도됩니다.
Eliezer Miron

8
구성에 따라 슬래시가 더 안전하지만 더 안전하지는 않지만 더 읽기 쉽습니다. 그러나 후자는 정규화 된 경로를 상대 경로로 변환하므로을 rstrip대신 사용하고 싶습니다 strip.
ari

7
파이썬 초보자를위한 @ari 주석을 보완합니다. I : strip('/')시작과 후행 '/'모두 rstrip('/')제거하고 후행 하나만 제거합니다
Titou

35

" 현재 디렉토리의 모든 서브 디렉토리 목록 가져 오기 .

다음은 Python 3 버전입니다.

import os

dir_list = next(os.walk('.'))[1]

print(dir_list)

2
매우 영리합니다. 효율성은 중요하지 않지만 ( ... 완전히 중요 합니다.)이 또는 glob 기반 생성기 표현 (s.rstrip("/") for s in glob(parent_dir+"*/"))이 더 시간 효율적 인지 궁금합니다 . 내 직관적 의혹이 있다는 것이다 stat()기반 os.walk()솔루션을 해야 뿌리깊은 빠른 쉘 스타일의 대체 (globbing)보다. 슬프게도, 나는 의지가 timeit없고 실제로 알아낼 것입니다.
세실 커리

3
그러면 상위 디렉토리 이름이 앞에 붙지 않은 서브 디렉토리 이름이 리턴됩니다.
Paul Chernoch '

19
import os, os.path

디렉토리에 (전체 경로) 즉시 하위 디렉토리를 가져 오려면 다음을 수행하십시오.

def SubDirPath (d):
    return filter(os.path.isdir, [os.path.join(d,f) for f in os.listdir(d)])

최신 (최신) 서브 디렉토리를 확보하려면 다음을 수행하십시오.

def LatestDirectory (d):
    return max(SubDirPath(d), key=os.path.getmtime)

목록 을 얻으려면 간단히 추가하십시오 list( filter(...) ).
user136036

12

os.walk 이 상황에서 당신의 친구입니다.

설명서에서 직접 :

walk ()는 트리를 위에서 아래로 또는 아래로 걸어서 디렉토리 트리에 파일 이름을 생성합니다. 디렉토리 상단 (상단 자체 포함)을 기반으로하는 트리의 각 디렉토리에 대해 3 개의 튜플 (dirpath, dirnames, filenames)이 생성됩니다.


1
첫 번째 레벨 서브 디렉토리 만 원하는 경우 첫 번째 리턴 값 세트 후에 os.walk 반복에서 벗어나십시오.
yoyo

11

이 방법은 한 번에 모두 잘 수행합니다.

from glob import glob
subd = [s.rstrip("/") for s in glob(parent_dir+"*/")]

7

Twisted의 FilePath 모듈 사용 :

from twisted.python.filepath import FilePath

def subdirs(pathObj):
    for subpath in pathObj.walk():
        if subpath.isdir():
            yield subpath

if __name__ == '__main__':
    for subdir in subdirs(FilePath(".")):
        print "Subdirectory:", subdir

일부 주석가는 이것을 위해 Twisted의 라이브러리를 사용하는 이점이 무엇인지 물었으므로 여기에서 원래 질문을 조금 넘어 보겠습니다.


FilePath의 장점을 설명하는 분기에 개선 된 설명서 가 있습니다 . 당신은 그것을 읽고 싶을 수도 있습니다.

이 예제에서보다 구체적으로 : 표준 라이브러리 버전과 달리이 함수는 imports없이 구현 될 수 있습니다 . "subdirs"함수는 인수에 대해서만 작동한다는 점에서 완전히 일반적입니다. 표준 라이브러리를 사용하여 파일을 복사하고 이동하려면 " open"내장, " listdir", 아마도 " isdir"또는 " os.walk"또는 " shutil.copy" 에 의존해야합니다 . " os.path.join"일 수도 있습니다. 실제 파일을 식별하기 위해 문자열을 인수로 전달해야한다는 사실은 말할 것도 없습니다. 각 디렉토리의 "index.tpl"을 "index.html"로 복사하는 전체 구현을 살펴 보겠습니다.

def copyTemplates(topdir):
    for subdir in subdirs(topdir):
        tpl = subdir.child("index.tpl")
        if tpl.exists():
            tpl.copyTo(subdir.child("index.html"))

위의 "subdirs"기능은 모든 FilePath유사한 객체 에서 작동 할 수 있습니다 . 이는 무엇보다도 ZipPath물체를 의미 합니다. 운수 나쁘게ZipPath 현재 읽기 전용이지만 쓰기를 지원하도록 확장 될 수 있습니다.

테스트 목적으로 자신의 객체를 전달할 수도 있습니다. 여기에 제안 된 os.path 사용 API를 테스트하려면 가져온 이름과 암시 적 종속성을 원숭이로 사용하고 일반적으로 테스트를 수행하기 위해 흑 마법을 수행해야합니다. FilePath를 사용하면 다음과 같은 작업을 수행 할 수 있습니다.

class MyFakePath:
    def child(self, name):
        "Return an appropriate child object"

    def walk(self):
        "Return an iterable of MyFakePath objects"

    def exists(self):
        "Return true or false, as appropriate to the test"

    def isdir(self):
        "Return true or false, as appropriate to the test"
...
subdirs(MyFakePath(...))

Twisted에 거의 노출되지 않았으므로 항상 추가 정보와 예제를 환영합니다. 이 대답은 그것을보기에 좋습니다. 그러나이 접근법은 내장 파이썬 모듈을 사용하는 것보다 훨씬 더 많은 작업이 필요하고 Twisted 설치이기 때문에이 기능을 사용하면 답변에 추가 할 수있는 이점이 있습니까?
Jarret Hardie

1
글리프의 대답은 아마도 TwistedLore가 .tpl 파일을 사용한다는 사실에서 영감을 얻은 것입니다.
Constantin

글쎄, 나는 분명히 스페인의 조사를 기대하지 않는다 :-) 나는 "* .tpl"이 "template"을 의미하는 추상 확장에 대한 일반적인 참조라고 생각했다. 결국 언어). 알아 둘만 한.
Jarret Hardie

Twisted ''FilePath '객체와'walk () '함수가 표준 API에 추가하는 것을 여전히 이해하고 싶지만 가능한 Twisted 각도로 작게 +1합니다.
Jarret Hardie

개인적으로 "os.walk는 dir, dirs, files의 3 튜플을 생성합니다"보다 "FilePath.walk ()는 경로 객체를 생성합니다"라는 것을 기억하기가 훨씬 쉽다는 것을 알게되었습니다. 그러나 다른 이점도 있습니다. FilePath는 다형성을 허용하므로 파일 시스템 이외의 것을 탐색 할 수 있습니다. 예를 들어 twisted.python.zippath.ZipArchive를 내 'subdirs'함수에 전달하고 FilePaths 대신 ZipPaths 생성기를 가져올 수 있습니다. 논리는 바뀌지 않지만 응용 프로그램은 이제 zip 파일을 마술처럼 처리합니다. 테스트하고 싶다면 객체를 제공하면되고 실제 파일을 작성할 필요는 없습니다.
글리프

4

방금 vmware 가상 머신을 이동하는 코드를 작성하고 하위 디렉토리 사이에서 파일 복사를 사용 os.path하고 shutil파일 복사를 완료했습니다.

def copy_client_files (file_src, file_dst):
    for file in os.listdir(file_src):
            print "Copying file: %s" % file
            shutil.copy(os.path.join(file_src, file), os.path.join(file_dst, file))

정말 우아하지는 않지만 작동합니다.


1

한 가지 방법이 있습니다.

import os
import shutil

def copy_over(path, from_name, to_name):
  for path, dirname, fnames in os.walk(path):
    for fname in fnames:
      if fname == from_name:
        shutil.copy(os.path.join(path, from_name), os.path.join(path, to_name))


copy_over('.', 'index.tpl', 'index.html')

-1 : shutil.copy가 현재 디렉토리에 복사되므로 작동하지 않으므로 하위 디렉토리 트리에서 찾은 각 'index.tpl'에 대해 현재 디렉토리의 'index.html'을 한 번 덮어 쓰게됩니다.
nosklo

1

경로를 언급해야 합니다. 자주 사용 라이브러리 .

직접 하위 디렉토리를 가져 오는 것은 다음과 같이 간단 해집니다.

my_dir.dirs()

전체 작업 예는 다음과 같습니다.

from path import Path

my_directory = Path("path/to/my/directory")

subdirs = my_directory.dirs()

NB : 경로는 문자열의 서브 클래스이지만 경로를 조작하는 유용한 방법을 많이 제공하므로 my_directory는 여전히 문자열로 조작 할 수 있습니다.


1
def get_folders_in_directories_recursively(directory, index=0):
    folder_list = list()
    parent_directory = directory

    for path, subdirs, _ in os.walk(directory):
        if not index:
            for sdirs in subdirs:
                folder_path = "{}/{}".format(path, sdirs)
                folder_list.append(folder_path)
        elif path[len(parent_directory):].count('/') + 1 == index:
            for sdirs in subdirs:
                folder_path = "{}/{}".format(path, sdirs)
                folder_list.append(folder_path)

    return folder_list

다음과 같은 함수를 호출 할 수 있습니다.

get_folders_in_directories_recursively (directory, index = 1)-> 첫 번째 레벨의 폴더 목록을 제공합니다

get_folders_in_directories_recursively (directory)-> 모든 하위 폴더를 제공합니다


python 3.6 버전은 훌륭하지만 내부 함수 변수에서 "자기"를 지워야합니다
locometro

1
클래스 내부에서 사용
중이고

0
import glob
import os

def child_dirs(path):
     cd = os.getcwd()        # save the current working directory
     os.chdir(path)          # change directory 
     dirs = glob.glob("*/")  # get all the subdirectories
     os.chdir(cd)            # change directory to the script original location
     return dirs

child_dirs함수는 디렉토리의 경로를 가져 와서 그 안에있는 서브 디렉토리 의 목록을 리턴 합니다.

dir
 |
  -- dir_1
  -- dir_2

child_dirs('dir') -> ['dir_1', 'dir_2']

0
import pathlib


def list_dir(dir):
    path = pathlib.Path(dir)
    dir = []
    try:
        for item in path.iterdir():
            if item.is_dir():
                dir.append(item)
        return dir
    except FileNotFoundError:
        print('Invalid directory')

0

pathlib를 사용하는 하나의 라이너 :

list_subfolders_with_paths = [p for p in pathlib.Path(path).iterdir() if p.is_dir()]
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.