我想将目录列表作为参数传递到我的 C# 控制台应用程序,这些参数可能是基于当前工作目录的相对路径,或者包含驱动器号和/或通配符。例如:
myApp.exe someDir another\dir dirs\with\wild*cards* ..\..\a\relative\dir C:\an\absolute\dir\with\wild*cards*
到目前为止,我已经想到了这个:
static void Main(string[] args)
{
List<string> directories = new List<string>();
foreach (string arg in args)
{
if (Directory.Exists(arg))
{
// 'arg' by itself is a valid directory, and must not contain any wildcards
// just add it to 'directories'
directories.Add(arg);
}
else
{
// 'arg' must either be a non-existant directory or invalid directory name,
// or else it contains wildcard(s). Find all matching directory names, starting
// at the current directory (assuming 'arg' might be a relative path), and add
// all matching directory names to 'directories'.
string[] dirs = Directory.GetDirectories(Directory.GetCurrentDirectory(), arg, SearchOption.TopDirectoryOnly);
directories.AddRange(dirs);
}
}
Console.WriteLine("Full list of directories specified by the command line args:");
foreach (string dir in directories)
{
Console.WriteLine(" " + dir);
}
// Now go do what I want to do for each of these directories...
}
这适用于 someDir
、another\dir
和 dirs\with\wild*cards*
,但不适用于..\..\a\relative\dir
或 C:\an\abolute\dir\with\wild*cards*
。对于相对目录,Directory.GetDirectories()
抛出一个 System.ArgumentException
说“搜索模式不能包含‘..’以向上移动目录并且只能包含在内部文件/目录名称,如'a..b'。”对于基于驱动器号的目录,它会抛出一个 System.ArgumentException
说“第二个路径片段不能是驱动器或 UNC 名称。”
如何处理“..”和驱动器号?这必须是一个已解决的问题,但我找不到 C# 的此类示例。
“奖励问题”:我上面的代码也不处理 a\path\with\wild*cards*\in\anything\other\than\the\top\directory
。还有什么简单的方法可以处理吗?
最佳答案
一些观察:
1) 要检查路径是绝对路径还是相对路径 - 您可以使用 Path.IsRooted()
2) 要将带有“..”的路径解析为绝对路径(无论是相对路径还是绝对路径),您可以使用:
path = new Uri(Path.Combine(Directory.GetCurrentDirectory(), path)).AbsolutePath;
通过 Uri
路由它会扩展这些点。 Path.GetFullPath
在使用通配符时会失败,但 Uri
不会。
3) 要检查路径是否包含通配符,您只需执行 path.Contains("*") || path.Contains("?")
因为这两个字符都不是有效的路径字符,所以除了作为通配符之外不能出现在上下文中。
4) 要解析绝对路径中的通配符(并匹配您的“奖励”要求),您需要找出第一个不包含通配符的目录。所以你基本上需要将路径分成几个部分 - 在第一个通配符之前和第一个通配符之后。例如:
C:\an\abolute\dir\with\wild*cards*
通配符之前的路径是 C:\an\abolute\dir\with
,在通配符之后(包括):wild*cards*
。
C:\an\abolu*e\dir\with\wild*cards*
第一个通配符之前的路径:C:\an
,之后的路径:abolu*e\dir\with\wild*cards*
。当然有不同的方法,我认为最简单的是正则表达式:
@"[\\/](?=[^\\/]*[\*?])"
它基本上匹配目录分隔符,但前提是它后面跟着 0 个或更多不是目录分隔符的字符,然后是通配符。
将所有这些结合在一起,我们有:
static IEnumerable<string> ResolveDirectories(string path) {
if (path.Contains("*") || path.Contains("?")) {
// we have a wildcard,
// resolve it to absolute path if necessary
if (!Path.IsPathRooted(path))
path = Path.Combine(Directory.GetCurrentDirectory(), path);
// resolve .. stuff if any
path = new Uri(Path.Combine(Directory.GetCurrentDirectory(), path)).AbsolutePath;
// split with regex above, only on first match (2 parts)
var parts = new Regex(@"[\\/](?=[^\\/]*[\*?])").Split(path, 2);
var searchRoot = parts[0];
var searchPatterns = parts[1].Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
foreach (var dir in ResolveWildcards(searchRoot, searchPatterns))
yield return dir;
}
else {
// this should work with "../.." type of paths too, will resolve relative to current directory
// so no processing is necessary
if (Directory.Exists(path)) {
yield return path;
}
else {
// invalid directory?
}
}
}
static IEnumerable<string> ResolveWildcards(string searchRoot, string [] searchPatterns) {
// if we have path C:\an\abolu*e\dir\with\wild*cards*
// search root is C:\an
// and patterns are: [abolu*e, dir, with, wild*cards*]
if (Directory.Exists(searchRoot)) {
// use next pattern to search in a search root
var next = searchPatterns[0];
// leave the rest for recursion
var rest = searchPatterns.Skip(1).ToArray();
foreach (var dir in Directory.EnumerateDirectories(searchRoot, next, SearchOption.AllDirectories)) {
// if nothing left (last pattern) - return it
if (rest.Length == 0)
yield return dir;
else {
// otherwise search with rest patterns in freshly found directory
foreach (var sub in ResolveWildcards(dir, rest))
yield return sub;
}
}
}
}
这没有经过适当的测试,所以要小心。
关于c# - 如何从可能包含驱动器号和/或通配符的 args 构建目录列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49656645/