Как использовать glob() для рекурсивного поиска файлов?

Это то, что у меня есть:

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

но я хочу искать подпапки src. Что-то вроде этого будет работать:

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

Но это явно ограничено и неуклюже.

Ответы

Ответ 1

Python 3. 5+

Поскольку вы находитесь на новом pathlib.Path.glob, вы должны использовать pathlib.Path.glob из модуля pathlib.

from pathlib import Path

for filename in Path('src').glob('**/*.c'):
    print(filename)

Если вы не хотите использовать pathlib, просто используйте glob.glob, но не забудьте передать параметр recursive ключевого слова.

Для случаев, когда совпадающие файлы начинаются с точки (.); как файлы в текущем каталоге или скрытые файлы в системе на основе Unix, используйте решение os.walk ниже.

Старые версии Python

Для более старых версий Python используйте os.walk для рекурсивного fnmatch.filter каталога и fnmatch.filter для сопоставления с простым выражением:

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

Ответ 2

Подобно другим решениям, но используя fnmatch.fnmatch вместо glob, поскольку os.walk уже перечисляет имена файлов:

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

Также, используя генератор, вы должны обрабатывать каждый файл, как он найден, вместо того, чтобы находить все файлы и затем обрабатывать их.

Ответ 3

Я модифицировал модуль glob для поддержки ** для рекурсивного globbing, например:

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

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

Полезно, если вы хотите предоставить своим пользователям возможность использовать синтаксис **, и, следовательно, os.walk() не достаточно хорош.

Ответ 4

Начиная с Python 3.4, можно использовать метод glob() одного из классов Path в новом pathlib, который поддерживает ** подстановочные знаки. Например:

from pathlib import Path

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

Update: Начиная с Python 3.5, тот же синтаксис также поддерживается glob.glob().

Ответ 5

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

fnmatch дает вам точно такие же шаблоны, как glob, так что это действительно отличная замена для glob.glob с очень близкой семантикой. Итеративная версия (например, генератор), IOW - замена для glob.iglob, - это тривиальная адаптация (просто yield промежуточные результаты по мере того, как вы идете, вместо extend, чтобы один список результатов возвращался в конце).

Ответ 6

Вы хотите использовать os.walk для сбора имен файлов, соответствующих вашим критериям. Например:

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

Ответ 7

Вот решение с вложенными списками, os.walk и простое совпадение суффикса вместо 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')]

Его можно сжать в однострочный:

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

или обобщен как функция:

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

Если вам нужны полные стили стиля glob, вы можете следить за Алексом и Пример Bruno и используйте 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')

Ответ 8

Недавно мне пришлось восстановить мои фотографии с расширением .jpg. Я запустил photorec и восстановил 4579 каталогов 2,2 миллиона файлов внутри, имея огромное разнообразие расширений. С script ниже я смог выбрать 50133 файлов havin.jpg расширение в течение минут:

#!/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)

Ответ 9

Йохан и Бруно предлагают отличные решения по минимальным требованиям, как указано. Я только что выпустил Formic, который реализует Ant FileSet и Globs, который может справиться с этим и более сложным сценарии. Реализация вашего требования:

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

Ответ 10

основанный на других ответах, это моя текущая рабочая реализация, которая извлекает вложенные XML файлы в корневой каталог:

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

Мне очень весело с python:)

Ответ 11

Рассмотрим pathlib.rglob().

Это похоже на вызов Path.glob() с добавлением "**/" перед данным относительным шаблоном:

import pathlib


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

Смотрите также @taleinat, связанный пост здесь и более ранний пост в другом месте.

Ответ 12

Другой способ сделать это, используя только модуль glob. Просто запустите метод rglob с исходным базовым каталогом и шаблоном для соответствия, и он вернет список совпадающих имен файлов.

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

Ответ 13

Просто сделал это.. он будет печатать файлы и каталог иерархическим способом

Но я не использовал fnmatch или ходить

#!/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)

Ответ 14

Этот использует fnmatch или регулярное выражение:

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

Ответ 15

В дополнение к предлагаемым ответам вы можете сделать это с помощью ленивого поколения и магии понимания списка:

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)

Помимо установки в одной строке и исключения ненужных списков в памяти, у этого также есть хороший побочный эффект, который вы можете использовать его так же, как оператор **, например, вы могли бы использовать os.path.join(root, 'some/path/*.c'), чтобы получить все .c файлы во всех подкаталогах src, которые имеют эту структуру.

Ответ 16

Упрощенная версия ответа Йохана Дахлина без 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']

Ответ 17

Или со списком:

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

Ответ 18

Вот мое решение, использующее понимание списков для поиска нескольких расширений файлов рекурсивно в каталоге и во всех подкаталогах:

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

Ответ 19

Для python> = 3.5 вы можете использовать **, recursive=True:

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

Demo


Если рекурсивный имеет значение true, шаблон ** будет соответствовать любым файлам и нулю или более directories и subdirectories. Если шаблон сопровождается os.sep, только каталоги и subdirectories совпадают.

Ответ 20

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)

Ответ 21

Я изменил верхний ответ в этой публикации.. и недавно создал этот script, который будет перебирать все файлы в данном каталоге (searchdir) и подкаталоги под ним... и печатает имя файла, rootdir, изменено/дату создания и размер.

Надеюсь, это поможет кому-то... и они могут ходить по каталогу и получать файлinfo.

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

Ответ 22

Вот решение, которое будет соответствовать шаблону в отношении полного пути, а не только базового имени файла.

Он использует fnmatch.translate, чтобы преобразовать шаблон стиля glob в регулярное выражение, которое затем сопоставляется с полным путем каждого файла найденный во время ходьбы по каталогу.

re.IGNORECASE является необязательным, но желательным для Windows, поскольку сама файловая система не чувствительна к регистру. (Я не собирался компилировать регулярное выражение, потому что документы указывают, что он должен быть кэширован внутренне.)

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

Ответ 23

Мне нужно решение для python 2.x, которое работает быстро на больших каталогах.
Я поддерживаю это:

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

Обратите внимание, что вам может понадобиться некоторая обработка исключений в случае, если ls не находит соответствующий файл.

Ответ 24

Для Python 3.5 и более поздних версий

file_names_array = glob.glob('src/*.c', recursive=True)

Редактировать: руководствуясь @NeStack, если описанное выше не работает, попробуйте

file_names_array = glob.glob('src/**.c', recursive=True)

дальше вам может понадобиться

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'