Làm thế nào để sử dụng global () để tìm các tệp đệ quy?


738

Đây là những gì tôi có:

glob(os.path.join('src','*.c'))

nhưng tôi muốn tìm kiếm các thư mục con của src. Một cái gì đó như thế này sẽ làm việc:

glob(os.path.join('src','*.c'))
glob(os.path.join('src','*','*.c'))
glob(os.path.join('src','*','*','*.c'))
glob(os.path.join('src','*','*','*','*.c'))

Nhưng điều này rõ ràng là hạn chế và vụng về.

Câu trả lời:


1355

Python 3,5+

Vì bạn đang ở trên một con trăn mới, bạn nên sử dụng pathlib.Path.rglobtừ pathlibmô-đun.

from pathlib import Path

for path in Path('src').rglob('*.c'):
    print(path.name)

Nếu bạn không muốn sử dụng pathlib, chỉ cần sử dụng glob.glob, nhưng đừng quên chuyển recursivetham số từ khóa.

Đối với trường hợp khớp tệp bắt đầu bằng dấu chấm (.); như các tệp trong thư mục hiện tại hoặc các tệp ẩn trên hệ thống dựa trên Unix, sử dụng os.walkgiải pháp bên dưới.

Các phiên bản Python cũ hơn

Đối với các phiên bản Python cũ hơn, hãy sử dụng os.walkđể đệ quy một thư mục và fnmatch.filterkhớp với một biểu thức đơn giản:

import fnmatch
import os

matches = []
for root, dirnames, filenames in os.walk('src'):
    for filename in fnmatch.filter(filenames, '*.c'):
        matches.append(os.path.join(root, filename))

3
Đối với Python cũ hơn 2.2, có os.path.walk()một chút khó sử dụng hơnos.walk()
John La Rooy

20
@gnibbler Tôi biết đó là một nhận xét cũ, nhưng nhận xét của tôi chỉ để cho mọi người biết rằng os.path.walk()nó không được dùng nữa và đã bị xóa trong Python 3.
Pedro Cunha

5
@DevC có thể hoạt động trong trường hợp cụ thể được hỏi trong câu hỏi này, nhưng thật dễ để tưởng tượng ai đó muốn sử dụng nó với các truy vấn như 'a * .c', v.v., vì vậy tôi nghĩ rằng đáng để giữ câu trả lời hơi chậm hiện tại.
Johan Dahlin

2
Đối với những gì nó có giá trị, trong trường hợp của tôi, việc tìm kiếm hơn 10.000 tệp với toàn cầu chậm hơn nhiều so với os.walk, vì vậy tôi đã đi với giải pháp sau vì lý do đó.
Godsmith

2
Đối với python 3,4, pathlib.Path('src').glob('**/*.c')nên làm việc.
CivilFan

111

Tương tự như các giải pháp khác, nhưng sử dụng fnmatch.fnmatch thay vì global, vì os.walk đã liệt kê tên tệp:

import os, fnmatch


def find_files(directory, pattern):
    for root, dirs, files in os.walk(directory):
        for basename in files:
            if fnmatch.fnmatch(basename, pattern):
                filename = os.path.join(root, basename)
                yield filename


for filename in find_files('src', '*.c'):
    print 'Found C source:', filename

Ngoài ra, việc sử dụng trình tạo cho phép bạn xử lý từng tệp khi tìm thấy, thay vì tìm tất cả các tệp rồi xử lý chúng.


3
bởi vì 1 lớp lót rất thú vị:reduce(lambda x, y: x+y, map(lambda (r,_,x):map(lambda f: r+'/'+f, filter(lambda f: fnmatch.fnmatch(f, pattern), x)), os.walk('src/webapp/test_scripts')))
njzk2

1
@ njzk2(os.path.join(root,filename) for root, dirs, files in os.walk(directory) for filename in files if fnmatch.fnmatch(filename, pattern))
Baldrickk

73

Tôi đã sửa đổi mô-đun toàn cầu để hỗ trợ ** cho toàn cầu đệ quy, ví dụ:

>>> import glob2
>>> all_header_files = glob2.glob('src/**/*.c')

https://github.com/miracle2k/python-glob2/

Hữu ích khi bạn muốn cung cấp cho người dùng khả năng sử dụng cú pháp ** và do đó, chỉ riêng os.walk () là không đủ.


2
Chúng ta có thể dừng lại sau khi nó tìm thấy trận đấu đầu tiên không? Có lẽ làm cho nó có thể sử dụng nó như một trình tạo chứ không phải là nó trả về một danh sách mọi kết quả có thể? Ngoài ra, đây là DFS hay BFS? Tôi nghĩ rằng tôi thích BFS hơn, vì vậy các tập tin gần gốc được tìm thấy trước tiên. +1 để tạo mô-đun này và cung cấp nó trên GitHub / pip.
ArtOfWarfare

14
Cú pháp ** đã được thêm vào mô đun toàn cầu chính thức trong Python 3.5.
ArtOfWarfare

@ArtOfWarfare Được rồi, tốt thôi. Điều này vẫn hữu ích cho <3.5.
cs95

1
Để kích hoạt tính năng đệ quy đệ quy bằng **mô-đun toàn cầu chính thức, hãy làm:glob(path, recursive=True)
winklerrr

68

Bắt đầu với Python 3.4, người ta có thể sử dụng glob()phương thức của một trong các Pathlớp trong mô đun pathlib mới , hỗ trợ các **ký tự đại diện. Ví dụ:

from pathlib import Path

for file_path in Path('src').glob('**/*.c'):
    print(file_path) # do whatever you need with these files

Cập nhật: Bắt đầu với Python 3.5, cú pháp tương tự cũng được hỗ trợ bởi glob.glob().


3
Thật vậy, và nó sẽ có trong Python 3.5 . Nó được cho là đã có trong Python 3.4, nhưng đã bị bỏ qua do nhầm lẫn .
taleinat 24/2/2015


Lưu ý rằng bạn cũng có thể sử dụng kết hợp pathlib.PurePath.relative_to để có được các đường dẫn tương đối. Xem câu trả lời của tôi ở đây để biết thêm bối cảnh.
pjgranahan

40
import os
import fnmatch


def recursive_glob(treeroot, pattern):
    results = []
    for base, dirs, files in os.walk(treeroot):
        goodfiles = fnmatch.filter(files, pattern)
        results.extend(os.path.join(base, f) for f in goodfiles)
    return results

fnmatchcung cấp cho bạn các mẫu chính xác giống như globvậy, vì vậy đây thực sự là một sự thay thế tuyệt vời cho glob.globngữ nghĩa rất gần. Phiên bản lặp (ví dụ: trình tạo), IOW thay thế glob.iglob, là một sự thích ứng tầm thường (chỉ yieldlà kết quả trung gian khi bạn đi, thay vì lấy extendmột danh sách kết quả duy nhất để trả về ở cuối).


1
Bạn nghĩ gì về việc sử dụng recursive_glob(pattern, treeroot='.')như tôi đề nghị trong chỉnh sửa của mình? Bằng cách này, nó có thể được gọi là ví dụ như recursive_glob('*.txt')và trực quan khớp với cú pháp của glob.
Chris Redford

@ChrisRedford, tôi cũng thấy đó là một vấn đề nhỏ. Hiện tại, nó phù hợp với thứ tự đối số "tệp sau đó mẫu" fnmatch.filter, gần như hữu ích như khả năng khớp đối số đơn glob.glob.
Alex Martelli

25

Đối với python> = 3,5 bạn có thể sử dụng **, recursive=True:

import glob
for x in glob.glob('path/**/*.c', recursive=True):
    print(x)

Bản giới thiệu


Nếu đệ quy là True, mẫu ** sẽ khớp với bất kỳ tệp nào và không hoặc nhiều hơn directoriessubdirectories . Nếu mẫu được theo sau bởi một os.sep, chỉ các thư mục và subdirectorieskhớp.


2
Điều này hoạt động tốt hơn pathlib.Path ('./ path /'). Toàn cầu (' * / ') vì nó cũng có trong thư mục có kích thước 0
Charles Walker

20

Bạn sẽ muốn sử dụng os.walkđể thu thập tên tệp phù hợp với tiêu chí của bạn. Ví dụ:

import os
cfiles = []
for root, dirs, files in os.walk('src'):
  for file in files:
    if file.endswith('.c'):
      cfiles.append(os.path.join(root, file))

15

Đây là một giải pháp với sự hiểu biết danh sách lồng nhau os.walkvà kết hợp hậu tố đơn giản thay vì glob:

import os
cfiles = [os.path.join(root, filename)
          for root, dirnames, filenames in os.walk('src')
          for filename in filenames if filename.endswith('.c')]

Nó có thể được nén thành một lớp lót:

import os;cfiles=[os.path.join(r,f) for r,d,fs in os.walk('src') for f in fs if f.endswith('.c')]

hoặc khái quát như là một hàm:

import os

def recursive_glob(rootdir='.', suffix=''):
    return [os.path.join(looproot, filename)
            for looproot, _, filenames in os.walk(rootdir)
            for filename in filenames if filename.endswith(suffix)]

cfiles = recursive_glob('src', '.c')

Nếu bạn cần globmẫu đầy đủ kiểu, bạn có thể làm theo ví dụ của Alex và Bruno và sử dụng fnmatch:

import fnmatch
import os

def recursive_glob(rootdir='.', pattern='*'):
    return [os.path.join(looproot, filename)
            for looproot, _, filenames in os.walk(rootdir)
            for filename in filenames
            if fnmatch.fnmatch(filename, pattern)]

cfiles = recursive_glob('src', '*.c')

7

Gần đây tôi đã phải khôi phục hình ảnh của mình với phần mở rộng .jpg. Tôi đã chạy photorec và khôi phục 4579 thư mục trong 2,2 triệu tệp, có rất nhiều tiện ích mở rộng. Với tập lệnh bên dưới, tôi có thể chọn 50133 tệp havin .jpg trong vòng vài phút:

#!/usr/binenv python2.7

import glob
import shutil
import os

src_dir = "/home/mustafa/Masaüstü/yedek"
dst_dir = "/home/mustafa/Genel/media"
for mediafile in glob.iglob(os.path.join(src_dir, "*", "*.jpg")): #"*" is for subdirectory
    shutil.copy(mediafile, dst_dir)

7

Hãy xem xét pathlib.rglob().

Điều này giống như gọi Path.glob()với "**/"được thêm vào trước mẫu tương đối đã cho:

import pathlib


for p in pathlib.Path("src").rglob("*.c"):
    print(p)

Xem thêm bài viết liên quan của @ taleinat tại đây và một bài tương tự ở nơi khác.


5

Johan và Bruno cung cấp các giải pháp tuyệt vời về yêu cầu tối thiểu như đã nêu. Tôi vừa phát hành Formic thực hiện Ant File Set và Globs có thể xử lý tình huống này và các tình huống phức tạp hơn. Việc thực hiện yêu cầu của bạn là:

import formic
fileset = formic.FileSet(include="/src/**/*.c")
for file_name in fileset.qualified_files():
    print file_name

1
Formic dường như bị bỏ rơi?! Và nó không hỗ trợ Python 3 ( bitbucket.org/aviser/formic/su/12/support-python-3 )
blueyed

5

dựa trên các câu trả lời khác, đây là cách triển khai công việc hiện tại của tôi, nó lấy các tệp xml lồng nhau trong một thư mục gốc:

files = []
for root, dirnames, filenames in os.walk(myDir):
    files.extend(glob.glob(root + "/*.xml"))

Tôi thực sự rất vui với python :)


3

Một cách khác để làm điều đó bằng cách chỉ sử dụng mô-đun toàn cầu. Chỉ cần gieo phương thức rglob với một thư mục cơ sở bắt đầu và một mẫu để khớp và nó sẽ trả về một danh sách các tên tệp phù hợp.

import glob
import os

def _getDirs(base):
    return [x for x in glob.iglob(os.path.join( base, '*')) if os.path.isdir(x) ]

def rglob(base, pattern):
    list = []
    list.extend(glob.glob(os.path.join(base,pattern)))
    dirs = _getDirs(base)
    if len(dirs):
        for d in dirs:
            list.extend(rglob(os.path.join(base,d), pattern))
    return list

3

Đối với python 3.5 trở lên

import glob

#file_names_array = glob.glob('path/*.c', recursive=True)
#above works for files directly at path/ as guided by NeStack

#updated version
file_names_array = glob.glob('path/**/*.c', recursive=True)

hơn nữa bạn có thể cần

for full_path_in_src in  file_names_array:
    print (full_path_in_src ) # be like 'abc/xyz.c'
    #Full system path of this would be like => 'path till src/abc/xyz.c'

3
Dòng mã đầu tiên của bạn không hoạt động để xem xét các thư mục con. Nhưng nếu bạn chỉ mở rộng nó bởi /**nó hoạt động với tôi, như thế:file_names_array = glob.glob('src/**/*.c', recursive=True)
NeStack

2

Hoặc với một sự hiểu biết danh sách:

 >>> base = r"c:\User\xtofl"
 >>> binfiles = [ os.path.join(base,f) 
            for base, _, files in os.walk(root) 
            for f in files if f.endswith(".jpg") ] 

2

Chỉ cần làm điều này .. nó sẽ in các tập tin và thư mục theo cách phân cấp

Nhưng tôi đã không sử dụng fnmatch hoặc đi bộ

#!/usr/bin/python

import os,glob,sys

def dirlist(path, c = 1):

        for i in glob.glob(os.path.join(path, "*")):
                if os.path.isfile(i):
                        filepath, filename = os.path.split(i)
                        print '----' *c + filename

                elif os.path.isdir(i):
                        dirname = os.path.basename(i)
                        print '----' *c + dirname
                        c+=1
                        dirlist(i,c)
                        c-=1


path = os.path.normpath(sys.argv[1])
print(os.path.basename(path))
dirlist(path)

2

Cái đó sử dụng biểu thức fnmatch hoặc thông thường:

import fnmatch, os

def filepaths(directory, pattern):
    for root, dirs, files in os.walk(directory):
        for basename in files:
            try:
                matched = pattern.match(basename)
            except AttributeError:
                matched = fnmatch.fnmatch(basename, pattern)
            if matched:
                yield os.path.join(root, basename)

# usage
if __name__ == '__main__':
    from pprint import pprint as pp
    import re
    path = r'/Users/hipertracker/app/myapp'
    pp([x for x in filepaths(path, re.compile(r'.*\.py$'))])
    pp([x for x in filepaths(path, '*.py')])

2

Ngoài các câu trả lời được đề xuất, bạn có thể thực hiện điều này với một số phép thuật hiểu và tạo danh sách lười biếng:

import os, glob, itertools

results = itertools.chain.from_iterable(glob.iglob(os.path.join(root,'*.c'))
                                               for root, dirs, files in os.walk('src'))

for f in results: print(f)

Bên cạnh việc khớp trong một dòng và tránh các danh sách không cần thiết trong bộ nhớ, điều này cũng có tác dụng phụ rất hay, đó là bạn có thể sử dụng nó theo cách tương tự như toán tử **, ví dụ: bạn có thể sử dụng os.path.join(root, 'some/path/*.c')để có được tất cả các tệp .c thư mục con của src có cấu trúc này.


2

Đây là một mã làm việc trên Python 2.7. Là một phần của các tín đồ của tôi hoạt động, tôi được yêu cầu viết một tập lệnh sẽ di chuyển các tệp cấu hình được đánh dấu bằng live-appName.properies sang appName.properies. Có thể có các tệp mở rộng khác cũng như live-appName.xml.

Dưới đây là một mã làm việc cho cái này, nó tìm các tệp trong các thư mục đã cho (mức lồng nhau) và sau đó đổi tên (di chuyển) nó thành tên tệp được yêu cầu

def flipProperties(searchDir):
   print "Flipping properties to point to live DB"
   for root, dirnames, filenames in os.walk(searchDir):
      for filename in fnmatch.filter(filenames, 'live-*.*'):
        targetFileName = os.path.join(root, filename.split("live-")[1])
        print "File "+ os.path.join(root, filename) + "will be moved to " + targetFileName
        shutil.move(os.path.join(root, filename), targetFileName)

Hàm này được gọi từ một tập lệnh chính

flipProperties(searchDir)

Hy vọng điều này sẽ giúp ai đó đấu tranh với các vấn đề tương tự.


1

Phiên bản đơn giản hóa câu trả lời của Johan Dahlin, không có fnmatch .

import os

matches = []
for root, dirnames, filenames in os.walk('src'):
  matches += [os.path.join(root, f) for f in filenames if f[-2:] == '.c']

1

Đây là giải pháp của tôi bằng cách sử dụng tính năng hiểu danh sách để tìm kiếm nhiều phần mở rộng tệp theo cách đệ quy trong một thư mục và tất cả các thư mục con:

import os, glob

def _globrec(path, *exts):
""" Glob recursively a directory and all subdirectories for multiple file extensions 
    Note: Glob is case-insensitive, i. e. for '\*.jpg' you will get files ending
    with .jpg and .JPG

    Parameters
    ----------
    path : str
        A directory name
    exts : tuple
        File extensions to glob for

    Returns
    -------
    files : list
        list of files matching extensions in exts in path and subfolders

    """
    dirs = [a[0] for a in os.walk(path)]
    f_filter = [d+e for d in dirs for e in exts]    
    return [f for files in [glob.iglob(files) for files in f_filter] for f in files]

my_pictures = _globrec(r'C:\Temp', '\*.jpg','\*.bmp','\*.png','\*.gif')
for f in my_pictures:
    print f

0
import sys, os, glob

dir_list = ["c:\\books\\heap"]

while len(dir_list) > 0:
    cur_dir = dir_list[0]
    del dir_list[0]
    list_of_files = glob.glob(cur_dir+'\\*')
    for book in list_of_files:
        if os.path.isfile(book):
            print(book)
        else:
            dir_list.append(book)

0

Tôi đã sửa đổi câu trả lời hàng đầu trong bài đăng này .. và gần đây đã tạo tập lệnh này sẽ lặp qua tất cả các tệp trong một thư mục nhất định (searchdir) và các thư mục con bên dưới nó ... và in tên tệp, rootdir, ngày sửa đổi / tạo và kích thước.

Hy vọng điều này sẽ giúp ai đó ... và họ có thể đi bộ thư mục và lấy fileinfo.

import time
import fnmatch
import os

def fileinfo(file):
    filename = os.path.basename(file)
    rootdir = os.path.dirname(file)
    lastmod = time.ctime(os.path.getmtime(file))
    creation = time.ctime(os.path.getctime(file))
    filesize = os.path.getsize(file)

    print "%s**\t%s\t%s\t%s\t%s" % (rootdir, filename, lastmod, creation, filesize)

searchdir = r'D:\Your\Directory\Root'
matches = []

for root, dirnames, filenames in os.walk(searchdir):
    ##  for filename in fnmatch.filter(filenames, '*.c'):
    for filename in filenames:
        ##      matches.append(os.path.join(root, filename))
        ##print matches
        fileinfo(os.path.join(root, filename))

0

Đây là một giải pháp sẽ khớp mẫu với đường dẫn đầy đủ và không chỉ tên tệp cơ sở.

Nó sử dụng fnmatch.translateđể chuyển đổi một kiểu mẫu toàn cầu thành một biểu thức chính quy, sau đó được khớp với đường dẫn đầy đủ của mỗi tệp được tìm thấy khi đi bộ thư mục.

re.IGNORECASElà tùy chọn, nhưng mong muốn trên Windows vì bản thân hệ thống tệp không phân biệt chữ hoa chữ thường. (Tôi không bận tâm biên dịch regex vì tài liệu cho biết nó nên được lưu trong bộ nhớ cache.)

import fnmatch
import os
import re

def findfiles(dir, pattern):
    patternregex = fnmatch.translate(pattern)
    for root, dirs, files in os.walk(dir):
        for basename in files:
            filename = os.path.join(root, basename)
            if re.search(patternregex, filename, re.IGNORECASE):
                yield filename

0

Tôi cần một giải pháp cho python 2.x hoạt động nhanh trên các thư mục lớn.
Tôi ủng hộ điều này:

import subprocess
foundfiles= subprocess.check_output("ls src/*.c src/**/*.c", shell=True)
for foundfile in foundfiles.splitlines():
    print foundfile

Lưu ý rằng bạn có thể cần một số xử lý ngoại lệ trong trường hợp lskhông tìm thấy bất kỳ tệp phù hợp.


Tôi chỉ nhận ra rằng ls src/**/*.cchỉ hoạt động nếu tùy chọn globalstar được bật ( shopt -s globstar) - xem câu trả lời này để biết chi tiết.
La Mã
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.