Matching paths

edit

Match a path

IsMatch checks whether a path matches the pattern. Pass the whole path, or the directory and the file name separately to avoid concatenating them:

var glob = Glob.Parse("src/**/*.cs", GlobDialect.Standard);

// The whole relative path
Assert.True(glob.IsMatch("src/App/Program.cs"));

// The directory and the file name, without concatenating them
Assert.True(glob.IsMatch("src/App", "Program.cs"));

// The kind of item, when the path alone does not say it
Assert.False(glob.IsMatch("src/App", "Folder.cs", PathItemType.Directory));
  • The overloads take a string or a ReadOnlySpan<char>.
  • Without a PathItemType, a path that ends with a separator is a directory, and any other path is a file. A pattern matches files or directories as described in Files and directories.
  • IsMatch only reads the text of the path: the file does not have to exist.

Use relative paths

A pattern is matched against the path from the start: src/*.cs matches src/Program.cs, not /home/me/project/src/Program.cs. Make the path relative to the directory the pattern is written for, for instance with Path.GetRelativePath, before you match it.

In the path, / is a separator on every operating system, and \ is one too on Windows. A pattern always uses /, except in the MSBuild dialect, which accepts both.

Skip the directories that cannot match

IsPartialMatch tells whether a directory can hold a path that matches the pattern. Use it to skip a whole directory when you walk a tree yourself:

var glob = Glob.Parse("src/**/*.cs", GlobDialect.Standard);

// A file below 'src/App' can match the pattern
Assert.True(glob.IsPartialMatch("src/App"));

// No file below 'docs' can match it
Assert.False(glob.IsPartialMatch("docs"));

IsPartialMatch returns true when it cannot rule the directory out: a true means "look inside", not "something matches". Enumerating files calls it for you.

Find the base directory of a pattern

TrySplitBasePath splits a pattern into its base path, the leading directories that hold no wildcard, and a pattern that matches the rest of the path:

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 _));
  • The base path uses / as the separator. It starts with a / for an absolute pattern, and with .. for an MSBuild pattern that goes up from the directory.
  • The last segment of the pattern is never part of the base path, even when it holds no wildcard.
  • With GlobOptions.IgnoreCase, a directory that holds a letter is not part of the base path: it does not have a single spelling. A Git pattern only has a base path when it is anchored, and a Posix pattern never has one.

Enumerating files uses it to start at the base directory.

Match patterns together

Glob and GlobCollection both implement IGlobEvaluatable, so IsMatch and IsPartialMatch work the same on a single pattern and on a collection of patterns. See Combining patterns.