How to implement glob in C#

后端 未结 4 607
逝去的感伤
逝去的感伤 2020-11-28 10:18

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

相关标签:
4条回答
  • 2020-11-28 10:29

    I stumbled upon the source to iron ruby that contains a pretty neat Glob class. It's fairly easy extract it from the related code.

    https://github.com/IronLanguages/main/blob/master/Languages/Ruby/Ruby/Builtins/Glob.cs

    0 讨论(0)
  • 2020-11-28 10:32
        /// <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);
        }
    
    0 讨论(0)
  • 2020-11-28 10:34

    it is easy with https://github.com/dazinator/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;
          }
        }
      }
    }
    
    0 讨论(0)
  • 2020-11-28 10:45

    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();
            }
    
        }
    }
    
    0 讨论(0)
提交回复
热议问题