I don't know if it's legit at StackOverflow to post your own answer to a question, but I saw nobody had asked this already. I went looking for a C# Glob and didn't find one, so I wrote one that others might find useful.
Asked
Active
Viewed 1.1k times
19
-
After some google-ling I found what glob is supposed to do. http://en.wikipedia.org/wiki/Glob_(programming) – tuinstoel Dec 29 '08 at 20:41
-
You would have gotten more points if you hadn't made it a community wiki. :-) – George Stocker Dec 30 '08 at 01:10
-
Why would I have gotten more points? I'm new here ... – Mark Maxham Jan 02 '09 at 03:27
-
Just for reference: Globs look like path\**\*.txt – Daniel Little Dec 18 '14 at 22:42
-
@Mark because Community Wiki answers don't award points, every upvote usually gives you 10 points. – Daniel Little Dec 18 '14 at 22:42
3 Answers
15
/// <summary>
/// return a list of files that matches some wildcard pattern, e.g.
/// C:\p4\software\dotnet\tools\*\*.sln to get all tool solution files
/// </summary>
/// <param name="glob">pattern to match</param>
/// <returns>all matching paths</returns>
public static IEnumerable<string> Glob(string glob)
{
foreach (string path in Glob(PathHead(glob) + DirSep, PathTail(glob)))
yield return path;
}
/// <summary>
/// uses 'head' and 'tail' -- 'head' has already been pattern-expanded
/// and 'tail' has not.
/// </summary>
/// <param name="head">wildcard-expanded</param>
/// <param name="tail">not yet wildcard-expanded</param>
/// <returns></returns>
public static IEnumerable<string> Glob(string head, string tail)
{
if (PathTail(tail) == tail)
foreach (string path in Directory.GetFiles(head, tail).OrderBy(s => s))
yield return path;
else
foreach (string dir in Directory.GetDirectories(head, PathHead(tail)).OrderBy(s => s))
foreach (string path in Glob(Path.Combine(head, dir), PathTail(tail)))
yield return path;
}
/// <summary>
/// shortcut
/// </summary>
static char DirSep = Path.DirectorySeparatorChar;
/// <summary>
/// return the first element of a file path
/// </summary>
/// <param name="path">file path</param>
/// <returns>first logical unit</returns>
static string PathHead(string path)
{
// handle case of \\share\vol\foo\bar -- return \\share\vol as 'head'
// because the dir stuff won't let you interrogate a server for its share list
// FIXME check behavior on Linux to see if this blows up -- I don't think so
if (path.StartsWith("" + DirSep + DirSep))
return path.Substring(0, 2) + path.Substring(2).Split(DirSep)[0] + DirSep + path.Substring(2).Split(DirSep)[1];
return path.Split(DirSep)[0];
}
/// <summary>
/// return everything but the first element of a file path
/// e.g. PathTail("C:\TEMP\foo.txt") = "TEMP\foo.txt"
/// </summary>
/// <param name="path">file path</param>
/// <returns>all but the first logical unit</returns>
static string PathTail(string path)
{
if (!path.Contains(DirSep))
return path;
return path.Substring(1 + PathHead(path).Length);
}

Mark Maxham
- 1,501
- 2
- 13
- 19
-
Bug? I had to replace "Path.Combine(head, dir)" with "dir" since Directory.GetDirectories already returns the full path. This caused a bug with paths like "..\SomeDir\*.dll" since "..\" were duplicated by Combine – jturcotte Mar 05 '09 at 16:08
-
1This doesn't seem to work if you pass a string like `*` to the `Glob` function. Are there some assumptions being made as to the sort of wildcard string it can handle? An absolute path maybe? – Ben Sep 30 '11 at 11:06
-
Method `Glob` splits the argument into two pieces at a `DirSep`. The code fails if there is no `Dirsep`. Adding the following statement to the beginning of method `PathHead` appears to work: `if (! path.Contains(DirSep)) {return ".";}`. – AdrianHHH Jul 30 '15 at 11:18
-
1@Ben The assumption seems to be that the string contains a `DirSep`. With the change in my previous comment the code works for me. – AdrianHHH Aug 17 '15 at 09:29
0
You can use the "dir" (aka "Get-ChildItem") powershell cmdlet from C#.
(I'm not saying whether you should.)
You have to add this reference to your project file (".csproj" or ".vcproj") manually:
<Reference Include="System.Management.Automation" />
See here for more details on how to use cmdlets from C#: http://www.devx.com/tips/Tip/42716
Here a working program:
using System;
using System.Collections.Generic;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Collections.ObjectModel;
namespace CsWildcard {
class Program {
static IEnumerable<string> CmdletDirGlobbing(string basePath, string glob){
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
// cd to basePath
if(basePath != null){
Pipeline cdPipeline = runspace.CreatePipeline();
Command cdCommand = new Command("cd");
cdCommand.Parameters.Add("Path", basePath);
cdPipeline.Commands.Add(cdCommand);
cdPipeline.Invoke(); // run the cmdlet
}
// run the "dir" cmdlet (e.g. "dir C:\*\*\*.txt" )
Pipeline dirPipeline = runspace.CreatePipeline();
Command dirCommand = new Command("dir");
dirCommand.Parameters.Add("Path", glob);
dirPipeline.Commands.Add(dirCommand);
Collection<PSObject> dirOutput = dirPipeline.Invoke();
// for each found file
foreach (PSObject psObject in dirOutput) {
PSMemberInfoCollection<PSPropertyInfo> a = psObject.Properties;
// look for the full path ("FullName")
foreach (PSPropertyInfo psPropertyInfo in psObject.Properties) {
if (psPropertyInfo.Name == "FullName") {
yield return psPropertyInfo.Value.ToString(); // yield it
}
}
}
}
static void Main(string[] args) {
foreach(string path in CmdletDirGlobbing(null,"C:\\*\\*\\*.txt")){
System.Console.WriteLine(path);
}
foreach (string path in CmdletDirGlobbing("C:\\", "*\\*\\*.exe")) {
System.Console.WriteLine(path);
}
Console.ReadKey();
}
}
}

Robert Fey
- 1,747
- 22
- 23
0
It is easy to implement with DotNet.Glob
Example:
public static class Glob
{
public static IEnumerable<FileInfo> Exec(DirectoryInfo dir, string glob)
{
var matcher = DotNet.Globbing.Glob.Parse(glob);
return dir.EnumerateAllFiles()
.Where(f => matcher.IsMatch(f.FullName));
}
public static IEnumerable<FileInfo> EnumerateAllFiles(this DirectoryInfo dir)
{
foreach(var f in dir.EnumerateFiles())
{
yield return f;
}
foreach(var sub in dir.EnumerateDirectories())
{
foreach(var f in EnumerateAllFiles(sub))
{
yield return f;
}
}
}
}
-
Where do you get the DirectoryInfo from? If I just have a string glob, I don't want to have to write the logic to pull out the base dir... – jjxtra Apr 13 '20 at 21:59
-
1You can have own version where the base dir is just a current directory of running process. Also example can be easily extended to support multiple base directories. – sergeyt Apr 15 '20 at 04:07
-
**'DirectoryInfo' does not contain a definition for 'EnumerateAllFiles'** error. – vee Jan 11 '21 at 12:30
-
@vee *EnumerateAllFiles* extension method is defined in the Glob class in my snippet. You can change it to non-extension version. I am not sure why you are getting this error. – sergeyt Jan 17 '21 at 14:13