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

Вот что у меня есть:

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

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

Ответы [ 23 ]

1163 голосов
/ 02 февраля 2010

Python 3,5 +

Поскольку вы находитесь на новом питоне, вы должны использовать 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 для сопоставления с простым выражением:

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))
104 голосов
/ 02 февраля 2010

Аналогично другим решениям, но с использованием 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

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

61 голосов
/ 26 июня 2011

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

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

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

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

59 голосов
/ 11 ноября 2014

Начиная с 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

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

39 голосов
/ 02 февраля 2010
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 с одним списком результатов для возврата в конце).

20 голосов
/ 02 февраля 2010

Вы захотите использовать 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))
15 голосов
/ 02 ноября 2011

Вот решение с вложенными списками, 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, вы можете следовать Алексу и Пример Бруно и использовать 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')
5 голосов
/ 05 января 2013

Недавно мне пришлось восстанавливать свои фотографии с расширением .jpg.Я запустил PhotoRec и восстановил 4579 каталогов, в которых находилось 2,2 миллиона файлов, с огромным разнообразием расширений. С помощью приведенного ниже сценария я смог выбрать 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)
5 голосов
/ 29 июля 2012

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

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

Мне действительно весело с python:)

5 голосов
/ 15 мая 2012

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

import formic
fileset = formic.FileSet(include="/src/**/*.c")
for file_name in fileset.qualified_files():
    print file_name
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...