Как я могу искать подпапки с помощью модуля glob.glob?

107

Я хочу открыть несколько подпапок в папке, найти несколько текстовых файлов и распечатать несколько строк текстовых файлов. Я использую это:

configfiles = glob.glob('C:/Users/sam/Desktop/file1/*.txt')

Но это также не может получить доступ к подпапкам. Кто-нибудь знает, как я могу использовать ту же команду для доступа к подпапкам?

UserYmY
источник

Ответы:

163

В Python 3.5 и новее используйте новую рекурсивную **/функциональность:

configfiles = glob.glob('C:/Users/sam/Desktop/file1/**/*.txt', recursive=True)

Если recursiveустановлен, **то разделитель путей соответствует 0 или более подкаталогам.

В более ранних версиях Python glob.glob()не может рекурсивно перечислять файлы в подкаталогах.

В этом случае я бы использовать в os.walk()сочетании с fnmatch.filter()вместо этого:

import os
import fnmatch

path = 'C:/Users/sam/Desktop/file1'

configfiles = [os.path.join(dirpath, f)
    for dirpath, dirnames, files in os.walk(path)
    for f in fnmatch.filter(files, '*.txt')]

Это будет рекурсивно просматривать ваши каталоги и возвращать все абсолютные пути к соответствующим .txtфайлам. В этом конкретном случае fnmatch.filter()может быть излишним, вы также можете использовать .endswith()тест:

import os

path = 'C:/Users/sam/Desktop/file1'

configfiles = [os.path.join(dirpath, f)
    for dirpath, dirnames, files in os.walk(path)
    for f in files if f.endswith('.txt')]
Мартейн Питерс
источник
3
Я вижу: glob.glob ('/ path to directory / * / *. Txt ") работает для меня. Это в основном использует правило оболочки Unix.
Сурья
7
@ User123: это не рекурсивный список каталогов . Вы перечисляете все текстовые файлы на один уровень , но не в других подкаталогах или даже непосредственно в них path to directory.
Мартин Питерс
1
Это не полностью связано, но почему настройка recursive=Falseвместе с **/ функциональностью не предоставляет список файлов только в данной папке, а скорее в ее дочерних?
Dr_Zaszuś
@ Dr_Zaszuś: прости? **/выдает список имен каталогов в текущем рабочем каталоге, так тчо в /, и с recursive=Falseвами в основном имеют двойное *, соответствие так же , как */, просто менее эффективно.
Martijn Pieters
@ Dr_Zaszuś: используйте, */*если вам нужны все файлы во всех подкаталогах.
Martijn Pieters
22

Чтобы найти файлы в непосредственных подкаталогах:

configfiles = glob.glob(r'C:\Users\sam\Desktop\*\*.txt')

Для рекурсивной версии, которая просматривает все подкаталоги, вы можете использовать **и передавать, recursive=True начиная с Python 3.5 :

configfiles = glob.glob(r'C:\Users\sam\Desktop\**\*.txt', recursive=True)

Оба вызова функций возвращают списки. Вы можете использовать glob.iglob()для возврата путей один за другим. Или используйтеpathlib :

from pathlib import Path

path = Path(r'C:\Users\sam\Desktop')
txt_files_only_subdirs = path.glob('*/*.txt')
txt_files_all_recursively = path.rglob('*.txt') # including the current dir

Оба метода возвращают итераторы (вы можете получать пути один за другим).

jfs
источник
Да, я понял это; но я тоже не ожидал glob()поддержки шаблонов в каталогах.
Мартейн Питерс
Комментарий удален, теперь я вижу, что он произвел неверное впечатление; кроме того, патч включает обновление документации для **случая рекурсии. Но для того, **чтобы работать, вы должны установить recursion=Trueпереключатель, кстати.
Мартейн Питерс
20

По этой теме много путаницы. Дайте мне посмотреть, смогу ли я это прояснить (Python 3.7):

  1. glob.glob('*.txt') :соответствует всем файлам, заканчивающимся на '.txt' в текущем каталоге
  2. glob.glob('*/*.txt') :как 1
  3. glob.glob('**/*.txt') :соответствует всем файлам, оканчивающимся на '.txt', только в непосредственных подкаталогах , но не в текущем каталоге
  4. glob.glob('*.txt',recursive=True) :как 1
  5. glob.glob('*/*.txt',recursive=True) :как 3
  6. glob.glob('**/*.txt',recursive=True):соответствует всем файлам, заканчивающимся на '.txt', в текущем каталоге и во всех подкаталогах

Так что лучше всегда указывать recursive=True.

зародыш
источник
1
Это должен быть главный ответ!
Абхик Саркар
9

Вы можете использовать Formic с Python 2.6

import formic
fileset = formic.FileSet(include="**/*.txt", directory="C:/Users/sam/Desktop/")

Раскрытие информации - я являюсь автором этого пакета.

Эндрю Алкок
источник
4

Вот адаптированная версия, которая включает glob.globаналогичные функции без использования glob2.

def find_files(directory, pattern='*'):
    if not os.path.exists(directory):
        raise ValueError("Directory not found {}".format(directory))

    matches = []
    for root, dirnames, filenames in os.walk(directory):
        for filename in filenames:
            full_path = os.path.join(root, filename)
            if fnmatch.filter([full_path], pattern):
                matches.append(os.path.join(root, filename))
    return matches

Итак, если у вас есть следующая структура каталога

tests/files
├── a0
   ├── a0.txt
   ├── a0.yaml
   └── b0
       ├── b0.yaml
       └── b00.yaml
└── a1

Вы можете сделать что-то вроде этого

files = utils.find_files('tests/files','**/b0/b*.yaml')
> ['tests/files/a0/b0/b0.yaml', 'tests/files/a0/b0/b00.yaml']

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

Cevaris
источник
2

configfiles = glob.glob('C:/Users/sam/Desktop/**/*.txt")

Не работает во всех случаях, вместо этого используйте glob2

configfiles = glob2.glob('C:/Users/sam/Desktop/**/*.txt")
НИЛЕШ КУМАР
источник
2

Если вы можете установить пакет glob2 ...

import glob2
filenames = glob2.glob("C:\\top_directory\\**\\*.ext")  # Where ext is a specific file extension
folders = glob2.glob("C:\\top_directory\\**\\")

Все имена файлов и папки:

all_ff = glob2.glob("C:\\top_directory\\**\\**")  
мрачный
источник
2

Если вы используете Python 3.4+, вы можете использовать pathlibмодуль. Path.glob()Метод поддерживает **шаблон, что означает «этот каталог и все подкаталоги, рекурсивно». Он возвращает генератор, дающий Pathобъекты для всех совпадающих файлов.

from pathlib import Path
configfiles = Path("C:/Users/sam/Desktop/file1/").glob("**/*.txt")
Евгений Ярмаш
источник
0

Как указал Мартин, glob может сделать это только с помощью **оператора, представленного в Python 3.5. Поскольку OP явно запросил модуль glob, следующее вернет итератор ленивой оценки, который ведет себя аналогично

import os, glob, itertools

configfiles = itertools.chain.from_iterable(glob.iglob(os.path.join(root,'*.txt'))
                         for root, dirs, files in os.walk('C:/Users/sam/Desktop/file1/'))

Обратите внимание, что configfilesв этом подходе вы можете повторять только один раз . Если вам нужен реальный список файлов конфигурации, которые можно использовать в нескольких операциях, вам придется создать его явно, используя list(configfiles).

f0xdx
источник
0

Команда rglobвыполнит бесконечную рекурсию на самом глубоком подуровне вашей структуры каталогов. Однако, если вам нужен только один уровень, не используйте его.

Я понимаю, что OP говорил об использовании glob.glob. Однако я считаю, что это отвечает намерению, которое заключается в рекурсивном поиске всех подпапок.

Эта rglobфункция недавно увеличила скорость в 100 раз для алгоритма обработки данных, который использовал структуру папок в качестве фиксированного допущения для порядка чтения данных. Однако rglobмы смогли выполнить одно сканирование всех файлов в указанном родительском каталоге или ниже, сохранить их имена в списке (более миллиона файлов), а затем использовать этот список, чтобы определить, какие файлы нам нужно открыть в любой момент. точки в будущем, основываясь только на соглашениях об именах файлов в зависимости от того, в какой папке они были.

братский голос
источник