Reputation: 48
I need to write a file glob that will match all files except for those that are contained within a certain folder (e.g. all files except those contained within the high level folder foo/
.
I've arrived at the following glob:
!(foo)/**/*
However, this glob doesn't seem to match on any files in Ruby's File.fnmatch
(even with FNM_PATHNAME
and FNM_DOTMATCH
set to true
.
Also, Ruby's glob interpreter seemed to have different behavior than JavaScript's:
JavaScript glob interpreter matches all strings
Ruby glob interpreter doesn't match any strings:
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
Upvotes: 3
Views: 2037
Reputation: 17065
If you really need to use a glob then you can list what is allowed, making it equivalent to the negation:
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?*}
means:
f
f
f
and whose second character is not a o
fo
fo
and whose third character is not a o
foo
if it has at least one more characterIf your string literal is too long it could become a pain to generate a glob that negates it, so why not make a function for it?
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?*}"
note: I didn't implement any glob escaping in this function because it seemed a little complicated. I may be wrong though
Upvotes: 3