Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
In this article, you'll learn how to use file globbing with the Microsoft.Extensions.FileSystemGlobbing NuGet package. A glob is a term used to define patterns for matching file and directory names based on wildcards. Globbing is the act of defining one or more glob patterns, and yielding files from either inclusive or exclusive matches.
Patterns
To match files in the file system based on user-defined patterns, start by instantiating a Matcher object. A Matcher can be instantiated with no parameters, or with a System.StringComparison parameter, which is used internally for comparing patterns to file names. The Matcher exposes the following additive methods:
Both AddExclude and AddInclude methods can be called any number of times, to add various file name patterns to either exclude or include from results. Once you've instantiated a Matcher and added patterns, it's then used to evaluate matches from a starting directory with the Matcher.Execute method.
Extension methods
The Matcher object has several extension methods.
Multiple exclusions
To add multiple exclude patterns, you can use:
Matcher matcher = new();
matcher.AddExclude("*.txt");
matcher.AddExclude("*.asciidoc");
matcher.AddExclude("*.md");
Alternatively, you can use the MatcherExtensions.AddExcludePatterns(Matcher, IEnumerable<String>[]) to add multiple exclude patterns in a single call:
Matcher matcher = new();
matcher.AddExcludePatterns(new [] { "*.txt", "*.asciidoc", "*.md" });
This extension method iterates over all of the provided patterns calling AddExclude on your behalf.
Multiple inclusions
To add multiple include patterns, you can use:
Matcher matcher = new();
matcher.AddInclude("*.txt");
matcher.AddInclude("*.asciidoc");
matcher.AddInclude("*.md");
Alternatively, you can use the MatcherExtensions.AddIncludePatterns(Matcher, IEnumerable<String>[]) to add multiple include patterns in a single call:
Matcher matcher = new();
matcher.AddIncludePatterns(new[] { "*.txt", "*.asciidoc", "*.md" });
This extension method iterates over all of the provided patterns calling AddInclude on your behalf.
Get all matching files
To get all matching files, you have to call Matcher.Execute(DirectoryInfoBase) either directly or indirectly. To call it directly, you need a search directory:
Matcher matcher = new();
matcher.AddIncludePatterns(new[] { "*.txt", "*.asciidoc", "*.md" });
string searchDirectory = "../starting-folder/";
PatternMatchingResult result = matcher.Execute(
new DirectoryInfoWrapper(
new DirectoryInfo(searchDirectory)));
// Use result.HasMatches and results.Files.
// The files in the results object are file paths relative to the search directory.
The preceding C# code:
- Instantiates a Matcher object.
- Calls AddIncludePatterns(Matcher, IEnumerable<String>[]) to add several file name patterns to include.
- Declares and assigns the search directory value.
- Instantiates a DirectoryInfo from the given
searchDirectory. - Instantiates a DirectoryInfoWrapper from the
DirectoryInfoit wraps. - Calls
Executegiven theDirectoryInfoWrapperinstance to yield a PatternMatchingResult object.
Note
The DirectoryInfoWrapper type is defined in the Microsoft.Extensions.FileSystemGlobbing.Abstractions namespace, and the DirectoryInfo type is defined in the System.IO namespace. To avoid unnecessary using directives, you can use the provided extension methods.
There is another extension method that yields an IEnumerable<string> representing the matching files:
Matcher matcher = new();
matcher.AddIncludePatterns(new[] { "*.txt", "*.asciidoc", "*.md" });
string searchDirectory = "../starting-folder/";
IEnumerable<string> matchingFiles = matcher.GetResultsInFullPath(searchDirectory);
// Use matchingFiles if there are any found.
// The files in this collection are fully qualified file system paths.
The preceding C# code:
- Instantiates a Matcher object.
- Calls AddIncludePatterns(Matcher, IEnumerable<String>[]) to add several file name patterns to include.
- Declares and assigns the search directory value.
- Calls
GetResultsInFullPathgiven thesearchDirectoryvalue to yield all matching files as aIEnumerable<string>.
Match overloads
The PatternMatchingResult object represents a collection of FilePatternMatch instances, and exposes a boolean value indicating whether the result has matchesβPatternMatchingResult.HasMatches.
With a Matcher instance, you can call any of the various Match overloads to get a pattern matching result. The Match methods invert the responsibility on the caller to provide a file or a collection of files in which to evaluate for matches. In other words, the caller is responsible for passing the file to match on.
Important
When using any of the Match overloads, there is no file system I/O involved. All of the file globbing is done in memory with the include and exclude patterns of the matcher instance. The parameters of the Match overloads do not have to be fully qualified paths. The current directory (Directory.GetCurrentDirectory()) is used when not specified.
To match a single file:
Matcher matcher = new();
matcher.AddInclude("**/*.md");
PatternMatchingResult result = matcher.Match("file.md");
The preceding C# code:
- Matches any file with the .md file extension, at an arbitrary directory depth.
- If a file named file.md exists in a subdirectory from the current directory:
result.HasMatcheswould betrue.- and
result.Fileswould have one match.
The additional Match overloads work in similar ways.
Ordered evaluation of include/exclude
By default, the matcher evaluates all include patterns first, then applies all exclude patterns, regardless of the order in which you added them. This means you can't re-include files that were previously excluded.
Starting in version 10 of the π¦ Microsoft.Extensions.FileSystemGlobbing package, you can opt into ordered evaluation, where includes and excludes are processed exactly in the sequence they were added:
using Microsoft.Extensions.FileSystemGlobbing;
// Preserve the order of patterns when matching.
Matcher matcher = new(preserveFilterOrder: true);
matcher.AddInclude("**/*"); // include everything
matcher.AddExclude("logs/**/*"); // exclude logs
matcher.AddInclude("logs/important/**/*"); // re-include important logs
var result = matcher.Execute(new DirectoryInfoWrapper(new DirectoryInfo(root)));
foreach (var file in result.Files)
{
Console.WriteLine(file.Path);
}
In this mode, patterns are applied one after another:
**/*adds all files.logs/**/*filters out anything inlogs/.logs/important/**/*adds back only files underlogs/important/.
Existing code that uses the default constructor will continue to run with the original "all includes, then all excludes" behavior.
Pattern formats
The patterns that are specified in the AddExclude and AddInclude methods can use the following formats to match multiple files or directories.
Exact directory or file name
some-file.txtpath/to/file.txt
Wildcards
*in file and directory names that represent zero to many characters not including separator characters.Value Description *.txtAll files with .txt file extension. *.*All files with an extension. *All files in top-level directory. .*File names beginning with '.'. *word*All files with 'word' in the filename. readme.*All files named 'readme' with any file extension. styles/*.cssAll files with extension '.css' in the directory 'styles/'. scripts/*/*All files in 'scripts/' or one level of subdirectory under 'scripts/'. images*/*All files in a folder with name that is or begins with 'images'. Arbitrary directory depth (
/**/).Value Description **/*All files in any subdirectory. dir/All files in any subdirectory under 'dir/'. dir/**/*All files in any subdirectory under 'dir/'. Relative paths.
To match all files in a directory named "shared" at the sibling level to the base directory given to Matcher.Execute(DirectoryInfoBase), use
../shared/*.
Examples
Consider the following example directory, and each file within its corresponding folder.
π parent
β file.md
β README.md
β
ββββπ child
β file.MD
β index.js
β more.md
β sample.mtext
β
ββββπ assets
β image.png
β image.svg
β
ββββπ grandchild
file.md
style.css
sub.text
Tip
Some file extensions are in uppercase, while others are in lowercase. By default, StringComparer.OrdinalIgnoreCase is used. To specify different string comparison behavior, use the Matcher.Matcher(StringComparison) constructor.
To get all of the markdown files, where the file extension is either .md or .mtext, regardless of character case:
Matcher matcher = new();
matcher.AddIncludePatterns(new[] { "**/*.md", "**/*.mtext" });
foreach (string file in matcher.GetResultsInFullPath("parent"))
{
Console.WriteLine(file);
}
Running the application would output results similar to the following:
C:\app\parent\file.md
C:\app\parent\README.md
C:\app\parent\child\file.MD
C:\app\parent\child\more.md
C:\app\parent\child\sample.mtext
C:\app\parent\child\grandchild\file.md
To get any files in an assets directory at an arbitrary depth:
Matcher matcher = new();
matcher.AddInclude("**/assets/**/*");
foreach (string file in matcher.GetResultsInFullPath("parent"))
{
Console.WriteLine(file);
}
Running the application would output results similar to the following:
C:\app\parent\child\assets\image.png
C:\app\parent\child\assets\image.svg
To get any files where the directory name contains the word child at an arbitrary depth, and the file extensions are not .md, .text, or .mtext:
Matcher matcher = new();
matcher.AddInclude("**/*child/**/*");
matcher.AddExcludePatterns(
new[]
{
"**/*.md", "**/*.text", "**/*.mtext"
});
foreach (string file in matcher.GetResultsInFullPath("parent"))
{
Console.WriteLine(file);
}
Running the application would output results similar to the following:
C:\app\parent\child\index.js
C:\app\parent\child\assets\image.png
C:\app\parent\child\assets\image.svg
C:\app\parent\child\grandchild\style.css