Как получить пути к файлам, соответствующие глобу, не имея их в файловой системе
У меня есть список путей к файлам относительно корневого каталога, и я пытаюсь определить, какой из них будет соответствовать шаблону глобуса. Я пытаюсь получить те же результаты, которые получил бы, если бы все файлы находились в моей файловой системе, и я запустилDir.glob(<my_glob_pattern>)
из корневого каталога.
Если это список путей к файлам:
foo/index.md
foo/bar/index.md
foo/bar/baz/index.md
foo/bar/baz/qux/index.md
и это шаблон шара:
foo/bar/*.md
Если файлы существовали в моей файловой системе, Dir.glob('foo/bar/*.md')
вернется только foo/bar/index.md
.
В glob
документы упоминают fnmatch
, и я попытался использовать его, но обнаружил, что шаблон foo/bar/*.md
соответствовал .md
файлы в любом количестве вложенных подкаталогов, подобных тому, что Dir.glob('foo/bar/**/*.md')
будет, а не только прямые дети foo/bar
каталог:
my_glob = 'foo/bar/*.md'
filepaths = [
'foo/index.md',
'foo/bar/index.md',
'foo/bar/baz/index.md',
'foo/bar/baz/qux/index.md',
]
# Using the provided filepaths
filepaths_that_match_pattern = filepaths.select{|path| File.fnmatch?(my_glob, path)}.sort
# If the filepaths actually existed on my filesystem
filepaths_found_by_glob = Dir.glob(my_glob).sort
raise Exception.new("They don't match!") unless filepaths_that_match_pattern == filepaths_found_by_glob
Я [неправильно] ожидал, что приведенный выше код будет работать, но filepaths_found_by_glob
содержит только прямых потомков, а filepaths_that_match_pattern
также содержит все вложенные дочерние элементы.
Как я могу получить такие же результаты, как Dir.glob
без указания путей к файлам в моей файловой системе?
2 ответа
Вы можете использовать флаг
File::FNM_PATHNAME
во время звонка
File.fnmatch
функция. Итак, ваш вызов функции будет выглядеть так:
File.fnmatch(pattern, path, File::FNM_PATHNAME)
Вы можете увидеть примеры, связанные с его использованием здесь: https://apidock.com/ruby/File/fnmatch/class
Не использовать File.fnmatch
вместо этого используйте Pathname.fnmatch
:
require 'pathname'
PATTERN = 'foo/bar/*.md'
%w[
foo/index.md
foo/bar/index.md
foo/bar/baz/index.md
foo/bar/baz/qux/index.md
].each do |p|
puts 'path: %-24s %s' % [
p,
Pathname.new(p).fnmatch(PATTERN) ? 'matches' : 'does not match'
]
end
# >> path: foo/index.md does not match
# >> path: foo/bar/index.md matches
# >> path: foo/bar/baz/index.md matches
# >> path: foo/bar/baz/qux/index.md matches
File предполагает наличие файлов или путей на диске, тогда как Pathname:
Путь представляет собой имя файла или каталога в файловой системе, но не сам файл.
Также относительно использования Dir.glob
: Будьте осторожны при использовании. Он немедленно пытается найти все совпадающие файлы или пути на диске и возвращает совпадения. На большом или медленном диске или с шаблоном, который плохо написан, например, при отладке или тестировании, ваш код может быть привязан на долгое время или заставить Ruby или машину, на которой работает Ruby, перейти в обход, и становится только хуже, если вы проверяете общий или удаленный диск. В качестве примера того, что может случиться, попробуйте следующее в командной строке, но будьте готовы ударить, Cntrl+C чтобы восстановить контроль:
ls /**/*
Вместо этого я рекомендую использовать класс Find в стандартной библиотеке, поскольку он будет перебирать совпадения. См. Эту документацию для примеров.