#python #recursion #filesystems #glob
#python #рекурсия #файловые системы #глоб
Вопрос:
Я пытаюсь сопоставить файлы с заданным расширением, используя шаблон glob. Функция glob (из модуля glob) имеет параметр ‘recursive’, который, похоже, должен включать или выключать поиск в подкаталогах. Однако мне не удалось создать шаблон glob, который будет сопоставлять файлы определенного типа в текущем каталоге, только если recursive = False и в подкаталогах тоже, если recursive = True.
folder='C:\test'
glob(folder '**.ext',recursive=True) #fails to find any files
glob(folder '\**.ext',recursive=True) #fails to search subfolders
glob(folder '\**',recursive=True) #fails to filter by type
glob(folder '\**\*.ext',recursive=False) #searches subfolders when it shouldn't
glob(folder '**\*.ext',recursive=True) #fails to search subfolders
На основе документации
«**» будет соответствовать любым файлам и нулю или более каталогов
Итак, я ожидал бы, что ** будет действовать как *, за исключением того, что он также соответствует косым чертам. Однако это работает не так, как ожидалось (см. Первые два примера выше).
Почему ** не работает должным образом и есть ли шаблон, который будет соответствовать определенным типам файлов, и рекурсию можно включить или выключить, используя только рекурсивный аргумент?
Ответ №1:
Я думаю, вы неправильно поняли значение recursive=True
. Он используется для того, чтобы **
выполнять поиск в подкаталогах с переменной глубиной — glob(folder '\**\*.ext',recursive=True)
будут найдены любые *.ext
файлы в начальной папке или где-либо под ней. Без этого recursive=True
, **
интерпретировалось бы обычным способом, причем каждое *
значение соответствовало бы любому количеству символов, исключая
, и **
поэтому было бы просто эквивалентно *
— другими словами, glob(folder '\**\*.ext',recursive=False)
выполнялся бы поиск любых *.ext
файлов, которые находятся ровно на одном уровне подкаталога ниже начальной папки, но не в самой начальной папке или в более глубоких подкаталогах.
То, что именно вам следует использовать, будет зависеть от того, что вы пытаетесь найти — и действительно может быть, что вам нужно выбирать между различными случаями с помощью if
инструкции — но нет варианта использования для использования **
с recursive=False
, потому что в этом случае это просто эквивалентно *
.
Наиболее вероятными полезными опциями являются:
glob(folder '\*.ext',recursive=False) # search in top folder only
glob(folder '\*\*.ext',recursive=False) # search exactly one level down
glob(folder '\**\*.ext',recursive=True) # search to arbitrary depth
,recursive=False
Можно опустить, поскольку он используется по умолчанию.
Если бы было ровно два варианта, из которых вы хотели выбрать логический вариант, вы всегда могли бы написать свою собственную функцию-оболочку, например:
def myglob(folder, pattern, subdirs=False):
if subdirs:
return glob(f'{folder}\**\{pattern}', recursive=True)
else
return glob(f'{folder}\{pattern}')
print(myglob(folder, '*.ext', subdirs=False))
print(myglob(folder, '*.ext', subdirs=True))
print(myglob(folder, '*.ext')) # same as subdirs=False in this example
(В Unix-подобных операционных системах, прочитайте /
для
выше.)
Комментарии:
1. Спасибо, я все еще не понимаю, почему первые два примера, которые я привел, не работают? Почему я не могу сделать что-то вроде третьего (который работает как ожидалось, переключаясь на основе значения recursive), а также фильтруя для данного расширения файла?
2. @KalevMaricq Насколько я могу судить, похоже, что, хотя
**
withrecursive=True
предназначено для сопоставления с любым количеством элементов пути, оно не будет соответствовать частям элементов пути — так что вам не следует делать**.ext
как часть шаблона glob.
Ответ №2:
Операция glob ‘**’
Исходный код glob можно найти здесь. Кроме комментариев, ‘**’ появляется только в _isrecursive функции:
def _isrecursive(pattern):
if isinstance(pattern, bytes):
return pattern == b'**'
else:
return pattern == '**'
Поскольку _isrecursive функция проверяет наличие ‘‘ с помощью оператора равенства, ‘‘ будет работать по назначению, только если это полный ‘шаблон’. _isrecursive функция вызывается 4 раза в исходном коде, один раз для всего глобуса и 3 раза для ‘basename’, который определяется как:
dirname, basename = os.path.split(pathname)
Это вызывается рекурсивно, разделяя хвосты пути по одному за раз, и каждый раз базовое имя — это все, что следует за косой чертой (технически os.sep, которая является для Windows или / для POSIX). Это означает, что для того, чтобы ‘**’ работало так, как описано, оно не может располагаться рядом ни с чем, кроме косых черт.
Если для параметра recursive явно не задано значение True, ‘**’ = ‘*’ ‘*’ = ‘*’ поскольку ‘*’ соответствует 0 или более символам без косой черты.
Сопоставление расширений
Из-за того, как работает ‘**’, он должен использоваться для представления всего уровня глобуса. Поскольку он представляет 0 или более уровней, r’folder***.ext’ будет сопоставлять файлы в ‘папке’, а также вложенные папки. Однако, без рекурсивного значения true, этот шаблон будет сопоставлять только файлы первого уровня вложенных папок.
‘**.ext’ не будет работать, потому что ‘**’ не одинок. Это оставляет следующие параметры:
-
Используйте другую библиотеку (например, os.walk, pathlib.glob и т.д.)
-
[f for f in glob(folder '\**',recursive=subdirs) if f.endswith('.ext')]
-
glob(folder ('\**' if subdirs else '') '\*.ext, recursive=True)
Или эквивалентно,
if recursive: glob(folder '\**\*.ext', recursive=True) else: glob(folder '\*.ext')
-
glob(folder '\..\**\*.ext', recursive=subdirs) #not recommended since it will also match files in sibling folders