재귀 하위 폴더 검색 및 목록 파이썬에서 파일 반환


117

메인 폴더의 하위 폴더를 재귀 적으로 살펴보고 특정 파일 형식으로 목록을 작성하는 스크립트를 작성 중입니다. 스크립트에 문제가 있습니다. 현재 다음과 같이 설정됩니다.

for root, subFolder, files in os.walk(PATH):
    for item in files:
        if item.endswith(".txt") :
            fileNamePath = str(os.path.join(root,subFolder,item))

문제는 subFolder 변수가 ITEM 파일이있는 폴더가 아닌 하위 폴더 목록을 가져 오는 것입니다. 나는 이전에 하위 폴더에 대해 for 루프를 실행하고 경로의 첫 번째 부분에 가입하려고 생각했지만 Id가 그 전에 제안 사항이 있는지 확인하기 위해 다시 확인했습니다. 당신의 도움을 주셔서 감사합니다!

답변:


156

당신은 dirpath당신이 부르는을 사용해야합니다 root. 은 dirnames당신이 원하는하지 않는 폴더가있는 경우 당신이 그것을 치다 수 공급 os.walk에 재귀하는가.

import os
result = [os.path.join(dp, f) for dp, dn, filenames in os.walk(PATH) for f in filenames if os.path.splitext(f)[1] == '.txt']

편집하다:

최근에 반대표를 던진 후 glob확장으로 선택하는 것이 더 나은 도구 라는 생각 이 들었습니다 .

import os
from glob import glob
result = [y for x in os.walk(PATH) for y in glob(os.path.join(x[0], '*.txt'))]

또한 생성기 버전

from itertools import chain
result = (chain.from_iterable(glob(os.path.join(x[0], '*.txt')) for x in os.walk('.')))

Python 3.4 이상용 Edit2

from pathlib import Path
result = list(Path(".").rglob("*.[tT][xX][tT]"))

1
'*. [Tt] [Xx] [Tt]'glob 패턴은 검색에서 대소 문자를 구분하지 않습니다.
SergiyKolesnikov

@SergiyKolesnikov, 감사합니다. 하단의 편집에서 사용했습니다. (가) 있습니다 rglob하지만 이식 민감하지 않다 - Windows 플랫폼에 영향을받지 않는다.
John La Rooy

1
@JohnLaRooy 그것도 함께 작동합니다 glob(여기에 Python 3.6) :glob.iglob(os.path.join(real_source_path, '**', '*.[xX][mM][lL]')
SergiyKolesnikov

@Sergiy : iglob하위 하위 폴더 이하의 파일에는 작동하지 않습니다. 추가해야합니다 recursive=True.
user136036

1
@ user136036, "더 좋음"이 항상 가장 빠른 것은 아닙니다. 때때로 가독성과 유지 보수성도 중요합니다.
John La Rooy

111

변경 Python 3.5 : "**"를 사용하는 재귀 glob 지원.

glob.glob() 새로 얻었다 재귀 매개 변수를.

모든 것을 얻고 싶다면 .txt 파일을 아래my_path (재귀 적으로 하위 디렉토리 포함) :

import glob

files = glob.glob(my_path + '/**/*.txt', recursive=True)

# my_path/     the dir
# **/       every file and dir under my_path
# *.txt     every file that ends with '.txt'

반복자가 필요하면 iglob 을 대안으로 사용할 수 있습니다 .

for file in glob.iglob(my_path, recursive=False):
    # ...

1
TypeError : glob ()에 예기치 않은 키워드 인수 'recursive'가 있습니다.
CyberJacob

1
작동해야합니다. 3.5 이상의 버전을 사용하고 있는지 확인하십시오. 자세한 내용은 내 답변에 문서 링크를 추가했습니다.
Rotareti 16.06.06

그래서 저는 2.7
CyberJacob에 있습니다.

1
왜 목록 이해력이 files = glob.glob(PATH + '/*/**/*.txt', recursive=True)아닌가?
tobltobs

이런! :) 완전히 중복됩니다. 내가 왜 그렇게 쓰게했는지 모르겠다. 언급 해 주셔서 감사합니다! 내가 고칠 게.
Rotareti

20

다른 사람이 이해하지 못하는 경우를 대비하여 John La Rooy의 목록 이해 를 중첩 대상으로 변환 합니다.

result = [y for x in os.walk(PATH) for y in glob(os.path.join(x[0], '*.txt'))]

다음과 동일해야합니다.

import glob

result = []

for x in os.walk(PATH):
    for y in glob.glob(os.path.join(x[0], '*.txt')):
        result.append(y)

다음은 목록 이해력os.walkglob.glob 함수에 대한 문서입니다 .


1
이 답변은 Python 3.7.3에서 저에게 효과적이었습니다. glob.glob(..., recursive=True)그리고 list(Path(dir).glob(...'))하지 않았다.
miguelmorin 19

11

이것은 내가 가지고 올 수있는 가장 빠른 해결책이 될 것으로 보인다이며 보다 빠른 os.walk훨씬 빨리 어떤 것보다 glob솔루션 .

  • 또한 기본적으로 무료로 모든 중첩 된 하위 폴더 목록을 제공합니다.
  • 여러 다른 확장을 검색 할 수 있습니다.
  • 또한 변경하여 파일 중 하나를 전체 경로 또는 단지 이름을 반환하도록 선택할 수 있습니다 f.pathf.name(하위 폴더를 변경하지 마십시오!).

Args : dir: str, ext: list .
함수 두 개의 목록을 반환 합니다.subfolders, files .

자세한 속도 분석은 아래를 참조하십시오.

def run_fast_scandir(dir, ext):    # dir: str, ext: list
    subfolders, files = [], []

    for f in os.scandir(dir):
        if f.is_dir():
            subfolders.append(f.path)
        if f.is_file():
            if os.path.splitext(f.name)[1].lower() in ext:
                files.append(f.path)


    for dir in list(subfolders):
        sf, f = run_fast_scandir(dir, ext)
        subfolders.extend(sf)
        files.extend(f)
    return subfolders, files


subfolders, files = run_fast_scandir(folder, [".jpg"])


속도 분석

모든 하위 폴더와 기본 폴더 내에서 특정 파일 확장자를 가진 모든 파일을 가져 오는 다양한 방법

tl; dr :
- fast_scandir분명히 승리하고 os.walk를 제외한 다른 모든 솔루션보다 두 배 빠릅니다.
- os.walk조금 느리게 2 위입니다.
-사용 glob하면 프로세스가 크게 느려집니다.
-어떤 결과도 자연 정렬을 사용하지 않습니다 . 즉, 결과가 다음과 같이 정렬됩니다 : 1, 10, 2. 자연스러운 정렬 (1, 2, 10)을 얻으려면 https://stackoverflow.com/a/48030307/2441026을 살펴보십시오.


결과 :

fast_scandir    took  499 ms. Found files: 16596. Found subfolders: 439
os.walk         took  589 ms. Found files: 16596
find_files      took  919 ms. Found files: 16596
glob.iglob      took  998 ms. Found files: 16596
glob.glob       took 1002 ms. Found files: 16596
pathlib.rglob   took 1041 ms. Found files: 16596
os.walk-glob    took 1043 ms. Found files: 16596

테스트는 W7x64, Python 3.8.1, 20 회 실행으로 수행되었습니다. 439 (부분적으로 중첩 된) 하위 폴더에 16596 개의 파일이 있습니다. https://stackoverflow.com/a/45646357/2441026
find_files 에서 왔으며 여러 확장을 검색 할 수 있습니다.
fast_scandir직접 작성했으며 하위 폴더 목록도 반환합니다. 검색 할 확장 목록을 제공 할 수 있습니다 (단순 항목에 하나의 항목이있는 목록을 테스트했는데 if ... == ".jpg"큰 차이가 없었습니다).


# -*- coding: utf-8 -*-
# Python 3


import time
import os
from glob import glob, iglob
from pathlib import Path


directory = r"<folder>"
RUNS = 20


def run_os_walk():
    a = time.time_ns()
    for i in range(RUNS):
        fu = [os.path.join(dp, f) for dp, dn, filenames in os.walk(directory) for f in filenames if
                  os.path.splitext(f)[1].lower() == '.jpg']
    print(f"os.walk\t\t\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found files: {len(fu)}")


def run_os_walk_glob():
    a = time.time_ns()
    for i in range(RUNS):
        fu = [y for x in os.walk(directory) for y in glob(os.path.join(x[0], '*.jpg'))]
    print(f"os.walk-glob\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found files: {len(fu)}")


def run_glob():
    a = time.time_ns()
    for i in range(RUNS):
        fu = glob(os.path.join(directory, '**', '*.jpg'), recursive=True)
    print(f"glob.glob\t\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found files: {len(fu)}")


def run_iglob():
    a = time.time_ns()
    for i in range(RUNS):
        fu = list(iglob(os.path.join(directory, '**', '*.jpg'), recursive=True))
    print(f"glob.iglob\t\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found files: {len(fu)}")


def run_pathlib_rglob():
    a = time.time_ns()
    for i in range(RUNS):
        fu = list(Path(directory).rglob("*.jpg"))
    print(f"pathlib.rglob\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found files: {len(fu)}")


def find_files(files, dirs=[], extensions=[]):
    # https://stackoverflow.com/a/45646357/2441026

    new_dirs = []
    for d in dirs:
        try:
            new_dirs += [ os.path.join(d, f) for f in os.listdir(d) ]
        except OSError:
            if os.path.splitext(d)[1].lower() in extensions:
                files.append(d)

    if new_dirs:
        find_files(files, new_dirs, extensions )
    else:
        return


def run_fast_scandir(dir, ext):    # dir: str, ext: list
    # https://stackoverflow.com/a/59803793/2441026

    subfolders, files = [], []

    for f in os.scandir(dir):
        if f.is_dir():
            subfolders.append(f.path)
        if f.is_file():
            if os.path.splitext(f.name)[1].lower() in ext:
                files.append(f.path)


    for dir in list(subfolders):
        sf, f = run_fast_scandir(dir, ext)
        subfolders.extend(sf)
        files.extend(f)
    return subfolders, files



if __name__ == '__main__':
    run_os_walk()
    run_os_walk_glob()
    run_glob()
    run_iglob()
    run_pathlib_rglob()


    a = time.time_ns()
    for i in range(RUNS):
        files = []
        find_files(files, dirs=[directory], extensions=[".jpg"])
    print(f"find_files\t\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found files: {len(files)}")


    a = time.time_ns()
    for i in range(RUNS):
        subf, files = run_fast_scandir(directory, [".jpg"])
    print(f"fast_scandir\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found files: {len(files)}. Found subfolders: {len(subf)}")

10

새로운 pathlib라이브러리는 이것을 한 줄로 단순화합니다.

from pathlib import Path
result = list(Path(PATH).glob('**/*.txt'))

생성기 버전을 사용할 수도 있습니다.

from pathlib import Path
for file in Path(PATH).glob('**/*.txt'):
    pass

이것은 Path거의 모든 용도로 사용할 수있는 객체를 반환 하거나 file.name.


6

가장 비단뱀적인 대답은 아니지만 재귀에 대한 깔끔한 교훈이기 때문에 재미로 여기에 넣을 것입니다.

def find_files( files, dirs=[], extensions=[]):
    new_dirs = []
    for d in dirs:
        try:
            new_dirs += [ os.path.join(d, f) for f in os.listdir(d) ]
        except OSError:
            if os.path.splitext(d)[1] in extensions:
                files.append(d)

    if new_dirs:
        find_files(files, new_dirs, extensions )
    else:
        return

내 컴퓨터에는 두 개의 폴더가 root있으며root2

mender@multivax ]ls -R root root2
root:
temp1 temp2

root/temp1:
temp1.1 temp1.2

root/temp1/temp1.1:
f1.mid

root/temp1/temp1.2:
f.mi  f.mid

root/temp2:
tmp.mid

root2:
dummie.txt temp3

root2/temp3:
song.mid

이 디렉토리 중 하나에서 .txt모든 .mid파일 을 찾고 싶다고 가정 해 봅시다.

files = []
find_files( files, dirs=['root','root2'], extensions=['.mid','.txt'] )
print(files)

#['root2/dummie.txt',
# 'root/temp2/tmp.mid',
# 'root2/temp3/song.mid',
# 'root/temp1/temp1.1/f1.mid',
# 'root/temp1/temp1.2/f.mid']

4

재귀는 Python 3.5의 새로운 기능이므로 Python 2.7에서는 작동하지 않습니다. 다음은 r문자열 을 사용하는 예 이므로 Win, Lin, ...에서있는 그대로 경로를 제공하면됩니다.

import glob

mypath=r"C:\Users\dj\Desktop\nba"

files = glob.glob(mypath + r'\**\*.py', recursive=True)
# print(files) # as list
for f in files:
    print(f) # nice looking single line per file

참고 : 파일의 깊이에 관계없이 모든 파일을 나열합니다.


3

이 방법으로 절대 경로 파일 목록을 반환 할 수 있습니다.

def list_files_recursive(path):
    """
    Function that receives as a parameter a directory path
    :return list_: File List and Its Absolute Paths
    """

    import os

    files = []

    # r = root, d = directories, f = files
    for r, d, f in os.walk(path):
        for file in f:
            files.append(os.path.join(r, file))

    lst = [file for file in files]
    return lst


if __name__ == '__main__':

    result = list_files_recursive('/tmp')
    print(result)

2

추가 라이트 라이브러리를 설치해도 괜찮다면 다음과 같이 할 수 있습니다.

pip install plazy

용법:

import plazy

txt_filter = lambda x : True if x.endswith('.txt') else False
files = plazy.list_files(root='data', filter_func=txt_filter, is_include_root=True)

결과는 다음과 같습니다.

['data/a.txt', 'data/b.txt', 'data/sub_dir/c.txt']

Python 2.7과 Python 3에서 모두 작동합니다.

Github : https://github.com/kyzas/plazy#list-files

면책 조항 : 저는의 저자입니다 plazy.


1

이 함수는 목록에 파일 만 재귀 적으로 넣습니다. 이것이 당신을 희망합니다.

import os


def ls_files(dir):
    files = list()
    for item in os.listdir(dir):
        abspath = os.path.join(dir, item)
        try:
            if os.path.isdir(abspath):
                files = files + ls_files(abspath)
            else:
                files.append(abspath)
        except FileNotFoundError as err:
            print('invalid directory\n', 'Error: ', err)
    return files

0

원래 솔루션은 거의 정확했지만 "root"변수는 반복적으로 경로를 따라 동적으로 업데이트됩니다. os.walk ()는 재귀 생성기입니다. (루트, 하위 폴더, 파일)의 각 튜플 세트는 설정 한 방식대로 특정 루트를위한 것입니다.

root = 'C:\\'
subFolder = ['Users', 'ProgramFiles', 'ProgramFiles (x86)', 'Windows', ...]
files = ['foo1.txt', 'foo2.txt', 'foo3.txt', ...]

root = 'C:\\Users\\'
subFolder = ['UserAccount1', 'UserAccount2', ...]
files = ['bar1.txt', 'bar2.txt', 'bar3.txt', ...]

...

전체 목록을 인쇄하기 위해 코드를 약간 수정했습니다.

import os
for root, subFolder, files in os.walk(PATH):
    for item in files:
        if item.endswith(".txt") :
            fileNamePath = str(os.path.join(root,item))
            print(fileNamePath)

도움이 되었기를 바랍니다!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.