Glob Class

Glob patterns specify sets of filenames with wildcard characters. Supported syntaxes:

WildcardDescription
*matches any number of characters including none, excluding directory separator
?matches a single character
[abc]matches one character in the brackets
[!abc]matches any character not in the brackets. GlobDialect.Git, GlobDialect.Posix and GlobDialect.PosixPath also accept [^abc].
[a-z]matches one character from the range given in the brackets. With GlobOptions.IgnoreCase, a character matches when it, its lowercase form or its uppercase form is in the range.
[!a-z]matches one character that is not from the range given in the brackets, using the same rule as [a-z] for GlobOptions.IgnoreCase.
[[:alpha:]]matches one character of a POSIX character class. Only supported by GlobDialect.Git, GlobDialect.Posix and GlobDialect.PosixPath. The supported classes are alnum, alpha, blank, cntrl, digit, graph, lower, print, punct, space, upper and xdigit. With GlobOptions.IgnoreCase, upper and lower match a letter of either case.
[[=a=]] [[.a.]]an equivalence class and a collating symbol, which both stand for the character they hold. Only supported by GlobDialect.Posix and GlobDialect.PosixPath. A collating symbol can be a range bound: [[.-.]-0].
{abc,*.cs}comma-delimited alternatives, matches 'abc' or a name ending with '.cs'. An alternative can hold wildcards, bracket expressions and other braces, and a '/' or a '**', as in {src/**,tests}/*.cs. Only supported by GlobDialect.Standard; the other dialects read the braces as ordinary characters.
**matches zero or more directories when it makes a whole path segment. A trailing ** matches at least one segment. The POSIX dialects read it as a *.
!patternleading '!' negates the pattern. Only supported by GlobDialect.Standard and GlobDialect.Git.
\xescapes the following character. For instance, '\*' matches the literal character '*' instead of being a wildcard. GlobDialect.Git and the POSIX dialects also read an escape in a bracket expression. GlobDialect.MSBuild reads '\' as a path separator and escapes a character with %XX instead.

If the pattern ends with a /, only directories are matched. Otherwise, only files are matched. A GlobDialect.Git pattern matches both a file and a directory, and one ending with a / matches the directory itself as well as everything below it. A GlobDialect.Posix pattern matches a plain string, whatever the kind of item.

GlobDialect.Git, GlobDialect.MSBuild, GlobDialect.Posix and GlobDialect.PosixPath follow the behavior of git, MSBuild and fnmatch(3), which the tests check against corpora produced by those implementations. The exceptions are deliberate: characters are compared as UTF-16 code units (git compares the bytes of their UTF-8 encoding), a path ending with a / is a directory (fnmatch compares strings), and a pattern that can never match anything, such as one holding an unknown character class, is rejected.

The cost of a single Glob.IsMatch call is polynomial in the length of the pattern and of the path, whatever the number of wildcards.

public sealed class Glob : IGlobEvaluatable

Example

Parse a glob pattern and check if a path matches:

var glob = Glob.Parse("src/**/*.txt", GlobDialect.Standard);
Assert.True(glob.IsMatch("src/folder/file.txt"));
Assert.False(glob.IsMatch("docs/file.txt"));

Enumerate files that match a glob pattern:

var glob = Glob.Parse("**/*.cs", GlobDialect.Standard, GlobOptions.IgnoreCase);
foreach (var file in glob.EnumerateFiles("C:/MyProject"))
{
    Console.WriteLine(file);
}

See also

Implements

Properties

NameDescription
Meziantou.Framework.Globbing.IGlobEvaluatable.CanMatchDirectories
Meziantou.Framework.Globbing.IGlobEvaluatable.CanMatchFiles
Meziantou.Framework.Globbing.IGlobEvaluatable.TraverseDirectories
ModeGets the glob mode indicating whether this pattern includes or excludes matches.

Meziantou.Framework.Globbing.IGlobEvaluatable.CanMatchDirectories

Glob.cs
bool IGlobEvaluatable.CanMatchDirectories { get; }

Property value

bool

No documentation is available.

Meziantou.Framework.Globbing.IGlobEvaluatable.CanMatchFiles

Glob.cs
bool IGlobEvaluatable.CanMatchFiles { get; }

Property value

bool

No documentation is available.

Meziantou.Framework.Globbing.IGlobEvaluatable.TraverseDirectories

Glob.cs
bool IGlobEvaluatable.TraverseDirectories { get; }

Property value

bool

No documentation is available.

Gets the glob mode indicating whether this pattern includes or excludes matches.

public GlobMode Mode { get; }

Property value

GlobMode

Methods

NameDescription
Escape(ReadOnlySpan<char>, GlobDialect)Escapes text so that it can be used as a pattern, or as a part of a pattern, that matches exactly this text.
Escape(string, GlobDialect)Escapes text so that it can be used as a pattern, or as a part of a pattern, that matches exactly this text.
IsMatch(ReadOnlySpan<char>, ReadOnlySpan<char>, PathItemType?)Determines whether the specified path matches this glob pattern.
IsPartialMatch(ReadOnlySpan<char>, ReadOnlySpan<char>)Determines whether a directory should be recursed into when enumerating files.
Parse(ReadOnlySpan<char>, GlobDialect, GlobOptions)Parses a glob pattern string.
Parse(string, GlobDialect, GlobOptions)Parses a glob pattern string.
ToString()Returns a pattern that parses back, with the same dialect and options, into a glob that matches the same paths.
TryParse(ReadOnlySpan<char>, GlobDialect, GlobOptions, Glob?)Attempts to parse a glob pattern string.
TryParse(string, GlobDialect, GlobOptions, Glob?)Attempts to parse a glob pattern string.
TrySplitBasePath(string?, Glob?)Splits the pattern into its base path, the leading directories that do not hold any wildcard, and a pattern that matches the rest of the path relative to that base path.

Escape overloads

Escape(ReadOnlySpan<char>, GlobDialect)

Glob.cs

Escapes text so that it can be used as a pattern, or as a part of a pattern, that matches exactly this text.

public static string Escape(ReadOnlySpan<char> text, GlobDialect dialect)
Parameters
text ReadOnlySpan<char>
The text to escape, such as a file name or a relative path.
dialect GlobDialect
The dialect of the pattern.
Returns

string A pattern that matches text.

Remarks

Escape(string, GlobDialect)

Glob.cs

Escapes text so that it can be used as a pattern, or as a part of a pattern, that matches exactly this text.

public static string Escape(string text, GlobDialect dialect)
Parameters
text string
The text to escape, such as a file name or a relative path.
dialect GlobDialect
The dialect of the pattern.
Returns

string A pattern that matches text.

Remarks

Every character that dialect reads as syntax is escaped, as well as a leading ! (and # for GlobDialect.Git) and a trailing space for GlobDialect.Git. GlobDialect.MSBuild also escapes the characters that have a meaning in a project file (;, $, @, ', ( and )).

A path separator stays a separator: /, \ on Windows, and both for GlobDialect.MSBuild. GlobDialect.Posix matches a plain string, so it has no separator. As for any GlobDialect.Git pattern, a result without a / matches the name at any depth; prepend a / to anchor it to the root.

Example
// Build a pattern from a folder name that holds special characters
var pattern = Glob.Escape("Reports [2024]", GlobDialect.Standard) + "/**/*.pdf";
Assert.Equal(@"Reports \[2024]/**/*.pdf", pattern);

var glob = Glob.Parse(pattern, GlobDialect.Standard);
Assert.True(glob.IsMatch("Reports [2024]/Q1/summary.pdf"));
Assert.False(glob.IsMatch("Reports 2/Q1/summary.pdf"));

// Each dialect has its own escaping rules
Assert.Equal("100%25 done.txt", Glob.Escape("100% done.txt", GlobDialect.MSBuild));
Assert.Equal(@"\#notes.txt", Glob.Escape("#notes.txt", GlobDialect.Git));

IsMatch(ReadOnlySpan<char>, ReadOnlySpan<char>, PathItemType?)

Glob.cs

Determines whether the specified path matches this glob pattern.

public bool IsMatch(ReadOnlySpan<char> directory, ReadOnlySpan<char> filename, PathItemType? itemType)

Parameters

directory ReadOnlySpan<char>
The directory part of the path to match.
filename ReadOnlySpan<char>
The filename part of the path to match.
itemType PathItemType?
The type of the path item (file or directory), or null if unknown.

Returns

bool true if the path matches the pattern; otherwise, false.

IsPartialMatch(ReadOnlySpan<char>, ReadOnlySpan<char>)

Glob.cs

Determines whether a directory should be recursed into when enumerating files.

public bool IsPartialMatch(ReadOnlySpan<char> folderPath, ReadOnlySpan<char> filename)

Parameters

folderPath ReadOnlySpan<char>
The folder path to check.
filename ReadOnlySpan<char>
The filename part of the path to check.

Returns

bool true if the directory could contain matches; otherwise, false.

Parse overloads

Parse(ReadOnlySpan<char>, GlobDialect, GlobOptions)

Glob.cs

Parses a glob pattern string.

public static Glob Parse(ReadOnlySpan<char> pattern, GlobDialect dialect, GlobOptions options = GlobOptions.None)
Parameters
pattern ReadOnlySpan<char>
The glob pattern to parse.
dialect GlobDialect
The glob pattern dialect to use.
options GlobOptions
Options for controlling pattern parsing behavior.
Returns

Glob A Glob object representing the parsed pattern.

Exceptions
ArgumentException
The pattern is invalid.

Parse(string, GlobDialect, GlobOptions)

Glob.cs

Parses a glob pattern string.

public static Glob Parse(string pattern, GlobDialect dialect, GlobOptions options = GlobOptions.None)
Parameters
pattern string
The glob pattern to parse.
dialect GlobDialect
The glob pattern dialect to use.
options GlobOptions
Options for controlling pattern parsing behavior.
Returns

Glob A Glob object representing the parsed pattern.

Exceptions
ArgumentException
The pattern is invalid.

ToString()

Glob.cs

Returns a pattern that parses back, with the same dialect and options, into a glob that matches the same paths.

public override string ToString()

Returns

string The pattern of this glob.

TryParse overloads

TryParse(ReadOnlySpan<char>, GlobDialect, GlobOptions, Glob?)

Glob.cs

Attempts to parse a glob pattern string.

public static bool TryParse(ReadOnlySpan<char> pattern, GlobDialect dialect, GlobOptions options, out Glob? result)
Parameters
pattern ReadOnlySpan<char>
The glob pattern to parse.
dialect GlobDialect
The glob pattern dialect to use.
options GlobOptions
Options for controlling pattern parsing behavior.
result Glob?
When this method returns, contains the parsed Glob if parsing succeeded, or null if parsing failed.
Returns

bool true if the pattern was parsed successfully; otherwise, false.

TryParse(string, GlobDialect, GlobOptions, Glob?)

Glob.cs

Attempts to parse a glob pattern string.

public static bool TryParse(string pattern, GlobDialect dialect, GlobOptions options, out Glob? result)
Parameters
pattern string
The glob pattern to parse.
dialect GlobDialect
The glob pattern dialect to use.
options GlobOptions
Options for controlling pattern parsing behavior.
result Glob?
When this method returns, contains the parsed Glob if parsing succeeded, or null if parsing failed.
Returns

bool true if the pattern was parsed successfully; otherwise, false.

TrySplitBasePath(string?, Glob?)

Glob.cs

Splits the pattern into its base path, the leading directories that do not hold any wildcard, and a pattern that matches the rest of the path relative to that base path.

public bool TrySplitBasePath(out string? basePath, out Glob? relativeGlob)

Parameters

basePath string?
When this method returns true, the base path, with / as the separator. It starts with a / for an absolute pattern, and with .. for an GlobDialect.MSBuild pattern that goes up from the directory.
relativeGlob Glob?
When this method returns true, a glob with the same dialect, options and mode that matches a path relative to basePath: a path basePath/path matches this glob when path matches relativeGlob.

Returns

bool true if the pattern starts with at least one literal directory; otherwise, false.

Remarks

The last segment of the pattern, which matches the name of the item, is never part of the base path. A directory is not part of the base path when it does not have a single spelling: one holding a letter when GlobOptions.IgnoreCase is set, a . or .. directory (except a leading .. for GlobDialect.MSBuild), or one holding a path separator of the current platform. A GlobDialect.Posix pattern never has a base path, and a GlobDialect.Git pattern has one only when it is anchored.

Example

var glob = Glob.Parse("src/app/**/*.cs", GlobDialect.Standard);
Assert.True(glob.TrySplitBasePath(out var basePath, out var relativeGlob));
Assert.Equal("src/app", basePath);
Assert.Equal("**/*.cs", relativeGlob.ToString());
Assert.True(relativeGlob.IsMatch("Models/User.cs"));

// MSBuild resolves a leading '..' against the directory
Assert.True(Glob.Parse("../shared/*.cs", GlobDialect.MSBuild).TrySplitBasePath(out basePath, out _));
Assert.Equal("../shared", basePath);

// The pattern starts with a wildcard
Assert.False(Glob.Parse("**/*.cs", GlobDialect.Standard).TrySplitBasePath(out _, out _));