#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?*}"
примечание: я не реализовывал экранирование глобуса в этой функции, потому что это казалось немного сложным. Хотя я могу ошибаться