파이썬 재귀 폴더 읽기


224

나는 C ++ / Obj-C 배경을 가지고 있으며 파이썬을 발견하고 있습니다 (약 1 시간 동안 작성했습니다). 폴더 구조에서 텍스트 파일의 내용을 재귀 적으로 읽는 스크립트를 작성 중입니다.

내가 가진 문제는 내가 작성한 코드가 한 폴더 깊이에서만 작동한다는 것입니다. 코드에서 이유를 볼 수 있습니다 ( #hardcoded path), 파이썬에 대한 나의 경험이 새로운 것이므로 파이썬으로 어떻게 나아갈 수 있는지 모르겠습니다.

파이썬 코드 :

import os
import sys

rootdir = sys.argv[1]

for root, subFolders, files in os.walk(rootdir):

    for folder in subFolders:
        outfileName = rootdir + "/" + folder + "/py-outfile.txt" # hardcoded path
        folderOut = open( outfileName, 'w' )
        print "outfileName is " + outfileName

        for file in files:
            filePath = rootdir + '/' + file
            f = open( filePath, 'r' )
            toWrite = f.read()
            print "Writing '" + toWrite + "' to" + filePath
            folderOut.write( toWrite )
            f.close()

        folderOut.close()

답변:


346

다음의 세 가지 반환 값을 이해해야합니다 os.walk.

for root, subdirs, files in os.walk(rootdir):

다음과 같은 의미가 있습니다.

  • root: "통과"되는 현재 경로
  • subdirs: root디렉토리 유형의 파일
  • files: 디렉토리 이외의 유형의 파일에 root있거나없는 파일subdirs

그리고 os.path.join슬래시로 연결 하는 대신 사용하십시오 ! 문제는 filePath = rootdir + '/' + file최상위 폴더 대신 현재 "걷는"폴더를 연결해야한다는 것입니다. 반드시이어야합니다 filePath = os.path.join(root, file). BTW "파일"은 내장되어 있으므로 일반적으로 변수 이름으로 사용하지 않습니다.

또 다른 문제는 다음과 같은 루프입니다.

import os
import sys

walk_dir = sys.argv[1]

print('walk_dir = ' + walk_dir)

# If your current working directory may change during script execution, it's recommended to
# immediately convert program arguments to an absolute path. Then the variable root below will
# be an absolute path as well. Example:
# walk_dir = os.path.abspath(walk_dir)
print('walk_dir (absolute) = ' + os.path.abspath(walk_dir))

for root, subdirs, files in os.walk(walk_dir):
    print('--\nroot = ' + root)
    list_file_path = os.path.join(root, 'my-directory-list.txt')
    print('list_file_path = ' + list_file_path)

    with open(list_file_path, 'wb') as list_file:
        for subdir in subdirs:
            print('\t- subdirectory ' + subdir)

        for filename in files:
            file_path = os.path.join(root, filename)

            print('\t- file %s (full path: %s)' % (filename, file_path))

            with open(file_path, 'rb') as f:
                f_content = f.read()
                list_file.write(('The file %s contains:\n' % filename).encode('utf-8'))
                list_file.write(f_content)
                list_file.write(b'\n')

당신이 모르는 경우, with파일에 대한 설명은 속기입니다.

with open('filename', 'rb') as f:
    dosomething()

# is effectively the same as

f = open('filename', 'rb')
try:
    dosomething()
finally:
    f.close()

4
무슨 일이 일어나고 있고 완벽하게 작동하는지 이해하기위한 훌륭한 지문. 감사! +1
Brock Woolf

16
나처럼 멍청한 / 명백한 누군가에게로 향합니다 ...이 코드 샘플은 각 디렉토리에 txt 파일을 씁니다. 정리 스크립트를 작성하는 데 필요한 모든 것이 여기에 있지만 버전 제어 폴더에서 테스트 한 것이 다행입니다.)
Steazy

그 두 번째 (가장 긴) 코드 스 니펫은 매우 잘 작동하여 많은 지루한 작업을
절약

1
속도가 가장 중요한 측면이라면 os.walk나쁘지 않지만,를 통해 더 빠른 방법을 생각해 냈습니다 os.scandir. 모든 glob솔루션은 walk& 보다 훨씬 느립니다 scandir. 내 기능과 전체 속도 분석은 여기에서 찾을 수 있습니다 : stackoverflow.com/a/59803793/2441026
user136036

112

Python 3.5 이상을 사용하는 경우 한 줄로 수행 할 수 있습니다.

import glob

for filename in glob.iglob(root_dir + '**/*.txt', recursive=True):
     print(filename)

설명서에 언급 된 바와 같이

재귀가 true 인 경우 패턴 '**'은 모든 파일 및 0 개 이상의 디렉토리 및 하위 디렉토리와 일치합니다.

모든 파일을 원한다면

import glob

for filename in glob.iglob(root_dir + '**/*', recursive=True):
     print(filename)

TypeError : iglob ()에 예기치 않은 키워드 인자 'recursive'가 있습니다
Jewenile

1
처음에 언급했듯이, 그것은 파이썬 3.5 이상에 대해서만
ChillarAnand

9
root_dir에는 슬래시가 있어야합니다. 그렇지 않으면 첫 번째 인수로 'folder / ** / *'대신 'folder ** / *'와 같은 것이 표시됩니다. os.path.join (root_dir, ' * / ')을 사용할 수 있지만 와일드 카드 경로와 함께 os.path.join을 사용할 수 있는지 여부는 알 수 없습니다 (응용 프로그램에서는 작동 함).
drojf

@ChillarAnand root_dir슬래시 가 필요한 이 답변의 코드에 주석을 추가 할 수 있습니까 ? 이것은 사람들의 시간을 절약 할 것입니다 (또는 적어도 시간을 절약했을 것입니다). 감사.
Dan Nissenbaum

1
대답과 같이 이것을 실행하면 재귀 적으로 작동하지 않았습니다. 이 작업을 재귀 적으로 수행하려면 다음과 같이 변경해야합니다 glob.iglob(root_dir + '**/**', recursive=True). 저는 Python 3.8.2에서 일하고 있습니다
mikey

38

Dave Webb에 동의 os.walk하면 트리의 각 디렉토리에 대한 항목이 생성됩니다. 사실, 당신은 신경 쓰지 않아도됩니다 subFolders.

다음과 같은 코드가 작동합니다.

import os
import sys

rootdir = sys.argv[1]

for folder, subs, files in os.walk(rootdir):
    with open(os.path.join(folder, 'python-outfile.txt'), 'w') as dest:
        for filename in files:
            with open(os.path.join(folder, filename), 'r') as src:
                dest.write(src.read())

3
좋은데 이것은 잘 작동합니다. 그러나 AndiDog의 버전은 더 길지만 파이썬 초보자에게 더 명확하게 이해하기 때문에 버전을 선호합니다. +1
Brock Woolf

20

TL; DR :find -type f 현재 폴더를 포함하여 아래의 모든 폴더에있는 모든 파일을 처리 하는 것과 같습니다 .

for currentpath, folders, files in os.walk('.'):
    for file in files:
        print(os.path.join(currentpath, file))

다른 답변에서 이미 언급했듯이 os.walk()그 대답이 있지만 더 잘 설명 할 수 있습니다. 아주 간단합니다! 이 트리를 살펴 보겠습니다.

docs/
└── doc1.odt
pics/
todo.txt

이 코드로 :

for currentpath, folders, files in os.walk('.'):
    print(currentpath)

currentpath그것을보고있는 현재 폴더입니다. 출력됩니다 :

.
./docs
./pics

따라서 세 개의 폴더가 있기 때문에 세 번 반복 됩니다. 현재 폴더 docs, 및 pics. 모든 루프에서는 변수 채우기 foldersfiles모든 폴더와 파일을. 그들에게 보여 주자 :

for currentpath, folders, files in os.walk('.'):
    print(currentpath, folders, files)

이것은 우리에게 보여줍니다 :

# currentpath  folders           files
.              ['pics', 'docs']  ['todo.txt']
./pics         []                []
./docs         []                ['doc1.odt']

첫 번째 줄에서 우리는 우리가 folder에 .있고, 그것은 두 개의 폴더를 포함 pics하고 docs, 하나의 파일이 있다는 것을 알 수 있습니다 todo.txt. 보시다시피 폴더는 자동으로 되풀이되고 하위 폴더의 파일 만 제공하기 때문에 해당 폴더로 되풀이하기 위해 아무 것도하지 않아도됩니다. 그리고 그 하위 폴더는 (이 예제에는 없지만)

모든 파일을 반복하고 싶다면 다음과 find -type f같습니다.

for currentpath, folders, files in os.walk('.'):
    for file in files:
        print(os.path.join(currentpath, file))

이 결과는 다음과 같습니다.

./todo.txt
./docs/doc1.odt

9

pathlib라이브러리 파일 작업을위한 정말 좋은 것입니다. 이렇게 Path객체 에 재귀 적 glob을 수행 할 수 있습니다 .

from pathlib import Path

for elem in Path('/path/to/my/files').rglob('*.*'):
    print(elem)

6

주어진 dir 아래의 모든 경로 목록을 원한다면 ( find .쉘 과 같이 ) :

   files = [ 
       os.path.join(parent, name)
       for (parent, subdirs, files) in os.walk(YOUR_DIRECTORY)
       for name in files + subdirs
   ]

기본 디렉토리 아래에 파일의 전체 경로 만 포함 시키려면 생략하십시오 + subdirs.


6
import glob
import os

root_dir = <root_dir_here>

for filename in glob.iglob(root_dir + '**/**', recursive=True):
    if os.path.isfile(filename):
        with open(filename,'r') as file:
            print(file.read())

**/**를 포함하여 모든 파일을 재귀 적으로 가져 오는 데 사용됩니다 directory.

if os.path.isfile(filename)filename변수가 file또는인지 확인하는 데 사용되며 directory파일 인 경우 해당 파일을 읽을 수 있습니다. 여기에 파일을 인쇄하고 있습니다.


6

다음이 가장 쉬운 것으로 나타났습니다.

from glob import glob
import os

files = [f for f in glob('rootdir/**', recursive=True) if os.path.isfile(f)]

Using glob('some/path/**', recursive=True)는 모든 파일을 가져 오지만 디렉토리 이름도 포함합니다. if os.path.isfile(f)조건을 추가하면 이 목록이 기존 파일로만 필터링됩니다.


3

os.path.join()경로를 만드는 데 사용 하십시오-더 깔끔합니다.

import os
import sys
rootdir = sys.argv[1]
for root, subFolders, files in os.walk(rootdir):
    for folder in subFolders:
        outfileName = os.path.join(root,folder,"py-outfile.txt")
        folderOut = open( outfileName, 'w' )
        print "outfileName is " + outfileName
        for file in files:
            filePath = os.path.join(root,file)
            toWrite = open( filePath).read()
            print "Writing '" + toWrite + "' to" + filePath
            folderOut.write( toWrite )
        folderOut.close()

이 코드는 2 단계 (또는 더 깊은) 폴더에서만 작동합니다. 여전히 그것은 나를 더 가깝게한다.
Brock Woolf

1

os.walk기본적으로 재귀 보행을 수행합니다. 각 dir에 대해 루트에서 시작하면 3 개의 튜플 (dirpath, dirnames, filenames)이 생성됩니다.

from os import walk
from os.path import splitext, join

def select_files(root, files):
    """
    simple logic here to filter out interesting files
    .py files in this example
    """

    selected_files = []

    for file in files:
        #do concatenation here to get full path 
        full_path = join(root, file)
        ext = splitext(file)[1]

        if ext == ".py":
            selected_files.append(full_path)

    return selected_files

def build_recursive_dir_tree(path):
    """
    path    -    where to begin folder scan
    """
    selected_files = []

    for root, dirs, files in walk(path):
        selected_files += select_files(root, files)

    return selected_files

1
파이썬 2.6에서 walk() 재귀 목록을 반환합니다. 코드를 사용해 보았고 반복 횟수가 많은 목록을 받았습니다 ... "# 하위 폴더에 대한 재귀 호출"이라는 주석 아래의 줄을 제거하면 문제없이 작동합니다.
borisbn

1

이 시도:

import os
import sys

for root, subdirs, files in os.walk(path):

    for file in os.listdir(root):

        filePath = os.path.join(root, file)

        if os.path.isdir(filePath):
            pass

        else:
            f = open (filePath, 'r')
            # Do Stuff

walk ()에서 디렉토리 목록을 파일과 디렉토리로 분할 한 경우 다른 listdir ()을 수행 한 다음 isdir ()을 수행하는 이유는 무엇입니까? 이것은 큰 나무에서는 다소 느릴 것 같습니다 (하나 대신 3 개의 syscall을 수행하십시오 : 1 = walk, 2 = listdir, 3 = isdir, 그냥 'subdirs'와 'files'를 걷고 반복하는 대신).
Luc

0

문제는 당신이 출력을 처리하지 않는다는 것입니다. os.walk 올바르게 것입니다.

먼저 다음을 변경하십시오.

filePath = rootdir + '/' + file

에:

filePath = root + '/' + file

rootdir고정 된 시작 디렉토리입니다. root에 의해 반환되는 디렉토리입니다os.walk 입니다.

둘째, 파일 처리 루프를 들여 쓸 필요가 없습니다. 각 하위 디렉토리에 대해이를 실행하는 것은 의미가 없기 때문입니다. 당신은 얻을 것이다 root각 하위 디렉토리로 설정합니다. 디렉토리 자체에서 무언가를 수행하지 않으려는 경우 서브 디렉토리를 수동으로 처리 할 필요가 없습니다.


각 하위 디렉토리에 데이터가 있으므로 각 디렉토리의 내용에 대해 별도의 텍스트 파일이 필요합니다.
Brock Woolf

@Brock : 파일 부분은 현재 디렉토리의 파일 목록입니다. 따라서 들여 쓰기는 실제로 잘못되었습니다. 당신은 드리고자합니다 filePath = rootdir + '/' + file, 맞아 소리가 나지 않는다 : 파일이 현재 파일 목록에서, 당신은 기존의 많은 파일에 기록되도록?
Alok Singhal
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.