Есть ли способ написать шаблон глобуса, который соответствует всем файлам, кроме файлов в папке?

#ruby #glob

Вопрос:

Мне нужно написать файловый глобус, который будет соответствовать всем файлам, кроме тех, которые содержатся в определенной папке (например, все файлы, кроме файлов, содержащихся в папке высокого уровня foo/ .

Я пришел к следующему глобусу:

 !(foo)/**/*
 

Однако этот глобус, похоже, не совпадает ни с какими файлами в Ruby File.fnmatch (даже с FNM_PATHNAME и FNM_DOTMATCH установлен в true .

Кроме того, интерпретатор глобуса Ruby, похоже, ведет себя иначе, чем JavaScript:

Интерпретатор JavaScript glob соответствует всем строкам

Интерпретатор Ruby glob не соответствует ни одной строке:

 2.6.2 :027 > File.fnmatch("!(foo)/**/*", "hello/world.js")
 => false
2.6.2 :028 > File.fnmatch("!(foo)/**/*", "test/some/globs")
 => false
2.6.2 :029 > File.fnmatch("!(foo)/**/*", "foo/bar/something.txt")
 => false
 

Комментарии:

1. Я не вижу документированного File::fnmatch отрицания шаблонов глобуса.

Ответ №1:

Если вам действительно нужно использовать глобус, вы можете перечислить, что разрешено, что эквивалентно отрицанию:

 extglob = "{[^f]*,f,f[^o]*,fo,fo[^o]*,foo?*}/**/*"

File.fnmatch(extglob, "hello/world.js", File::FNM_EXTGLOB | File::FNM_PATHNAME)
#=> true

File.fnmatch(extglob, "test/some/globs", File::FNM_EXTGLOB | File::FNM_PATHNAME)
#=> true

File.fnmatch(extglob, "foo/bar/something.txt", File::FNM_EXTGLOB | File::FNM_PATHNAME)
#=> false

File.fnmatch(extglob, "food/bar/something.txt", File::FNM_EXTGLOB | File::FNM_PATHNAME)
#=> true
 

{[^f]*,f,f[^o]*,fo,fo[^o]*,foo?*} означает:

  • Любая строка, которая не начинается с f
  • Strinf f
  • Любая строка, начинающаяся с f и второй символ которой не является o
  • Строка fo
  • Любая строка, начинающаяся с fo и третий символ которой не является o
  • Любая строка, начинающаяся с foo , если в ней есть хотя бы еще один символ

Обновить

Если ваш строковый литерал слишком длинный, может возникнуть проблема с созданием глобуса, который его отрицает, так почему бы не создать для него функцию?

 def extglob_neg str
  str.each_char.with_index.with_object([]) do |(_,i),arr|
    arr << "#{str[0,i]}[^#{str[i]}]*"
    arr << str[0..i]
  end.join(',').prepend('{').concat('?*}')
end

extglob_neg "Food"
#=> "{[^F]*,F,F[^o]*,Fo,Fo[^o]*,Foo,Foo[^d]*,Food?*}"
 

примечание: я не реализовывал экранирование глобуса в этой функции, потому что это казалось немного сложным. Хотя я могу ошибаться