Как получить пути к файлам, соответствующие глобу, не имея их в файловой системе

У меня есть список путей к файлам относительно корневого каталога, и я пытаюсь определить, какой из них будет соответствовать шаблону глобуса. Я пытаюсь получить те же результаты, которые получил бы, если бы все файлы находились в моей файловой системе, и я запустил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 в стандартной библиотеке, поскольку он будет перебирать совпадения. См. Эту документацию для примеров.

Другие вопросы по тегам