Is there a wildcard expansion option for .net apps? - c#

I've used the setargv.obj linking for Expanding Wildcard Arguments in the past for a number of C and C++ apps, but I can't find any similar mention for .net applications.
Is there a standard way to have your app's command line parameters automatically wildcard expanded? (i.e. expand *.doc from one entry in args parameter to all that match that wildcard).
P.S. I've hacked something together with Directory.GetFiles() for my current little project, but it does not cover wildcards with paths (yet), and it would be nice to do it without custom code.
Update: here is my rough hack, for illustration. It needs to split the parameters for the path and name for the GetFiles(), but this is a general idea. Linking setargv.obj into a C or C++ app would basically do all the wildcard expansion, leaving the user to only iterate over the argv array.
public static void Main(string[] args)
{
foreach (string argString in args)
{
// Split into path and wildcard
int lastBackslashPos = argString.LastIndexOf('\\') + 1;
path = argString.Substring(0, lastBackslashPos);
filenameOnly = argString.Substring(lastBackslashPos, argString.Length - lastBackslashPos);
string[] fileList = System.IO.Directory.GetFiles(path, filenameOnly);
foreach (string fileName in fileList)
{
// do things for each file
}
}
}

Here us my rough hack. I'd love for it to be recursive. And having experienced the shortcoming of Windows wildcards I might decide to use regular expressions rather than letting GetFiles() do it for me.
using System.IO;
public static string[] ExpandFilePaths(string[] args)
{
var fileList = new List<string>();
foreach (var arg in args)
{
var substitutedArg = System.Environment.ExpandEnvironmentVariables(arg);
var dirPart = Path.GetDirectoryName(substitutedArg);
if (dirPart.Length == 0)
dirPart = ".";
var filePart = Path.GetFileName(substitutedArg);
foreach (var filepath in Directory.GetFiles(dirPart, filePart))
fileList.Add(filepath);
}
return fileList.ToArray();
}

I'm not sure exactly what you're after... but if I get where you're going with the Directory.GetFiles() "hack" you mentioned, then something like this might work:
var Dirs = Directory.GetDirectories(#"C:\Windows", "sys*",
SearchOption.TopDirectoryOnly).ToList();
var Files = new List<String>();
Dirs.ForEach(dirName => Files.AddRange(Directory.GetFiles(dirName, "*.sys", SearchOption.AllDirectories)));
The wildcard option on the GetDirectories call will allow you to grab all the directories contained in the Windows folder [directly] that match the pattern "sys*".
You can then iterate over those folders grabbing all the files that match the pattern "*.sys".
Is that the kind of thing you're looking for? To automatically expand the args, you'd have to extract the wildcards in some kind of meaningful manner and apply them to that model...
For instance:
RunMyApp "C:\Windows\Sys*\ *.sys"
You'd pull out the string C:\Windows - probably with a regular expression, find the lowest level directory that doesn't contain a wildcard and apply it to the GetDirectories method, attaching the wildcarded string as the search parameter.
Then if your end of string (in this case *.sys) as the search pattern for Directory.GetFiles.
If you wanted to get more complicated and do something like:
C:\Windows\*\Sys*\*.sys
You would use the SearchOptions to set this behaviour:
Directory.GetDirectories(#"C:\Windows", "sys*", SearchOptions.AllDirectories)
This would grab all directories that matched the sys* wildcard in the Windows directory and all directories below it.
If you wanted to get much more complicated than that, then I'm not sure how you would do that... for instance, say you wanted folders that are contained by folders directly inside the Windows directory - I have no idea how you would go about something like that I'm afraid...I don't imagine exporting the entire tree structure to XML and using XPath to do it would be so efficient - the XPath would be fabulously simple for parsing out using wildcards - but converting to XML wouldn't be so efficient...

see the source code of disassembled Microsoft.Build.Shared.FileMatcher class in Microsoft.Build.dll
you can get some idea from the implementation of method GetFiles.
as a client you may use the method as follows
var filelist = FileMatcher.GetFiles(#"c:\alaki",#"c:\alaki\**\bin\*.dll");

Your code looks like exactly how you're supposed to do it.

Related

C# - Getting file names starting with a specific format in a directory

I've a directory with tons of files and I want only to get the names of the ones starting with sly_.
If I'm not wrong, the patter for this is ^sly_.
This is my try using the solution of this question:
string pattern = #"^sly_";
var matches = Directory.GetFiles(#"D:\mypath").Where(path => Regex.Match(path, pattern).Success);
foreach (string file in matches)
Console.Write(file);
Unfortunatelly, this doesn't list the files matching my pattern. So, can someone tell me what's wrong with me code and how can I list the file names starting with sly_?
Thanks in advance.
If you insist on regular expression you should test FileName, not the entire path:
string pattern = #"^sly_";
var matches = Directory
.GetFiles(#"D:\mypath")
.Where(path => Regex.IsMatch(Path.GetFileName(path), pattern));
Console.Write(String.Join(Environment.NewLine, matches));
Your actual issue is that Directory.GetFiles returns
An array of the full names (including paths) for the files in the specified directory, or an empty array if no files are found.
You regex would need to check for the D:\mypath part as well as the sly_ part. Other than that, your expression is correct.
You don't need to use regex at all. This is more readable and efficient:
string[] matches = Directory.GetFiles(#"D:\mypath", "sly_*");
Directory.GetFiles Method (String, String)
* is a wildcard for "zero or more characters in that position" and it's used only one the file-name not the full-path. If you wanted to include the extension:
string[] matches = Directory.GetFiles(#"D:\mypath", "sly_*.txt");
Your regex would also work if you just use the file-name not the full-path:
var matches = Directory.GetFiles(#"D:\mypath")
.Where(path => Regex.Match(Path.GetFileName(path), pattern).Success);
But as mentioned, this is less readable and not efficient. Remember that matches currently is only a LINQ query, not a collection. You need to add f.e. ToArray to get one. Otherwise this query is executed always when you use matches.
This is easy using Linq and classes DirectoryInfo and FileInfo. A FileInfo has properties FileName and FullFileName. Usage would be as follows:
IEnumerable<FileInfo> myFiles = new DirectoryInfo(#"D:\mypath")
.EnumerateFiles()
.Where(fileInfo => fileInfo.Name.StartsWith("sly_", StringComparison.OrdinalIgnoreCase));
Use Enumerable.Select to get the sequence with full file names or short file names
Why your code is not working is, Directory.GetFiles() returns the full path of files, like
D:\mypath\sly_yourFile.txt
So, the string path doesn't start with sly_ and does not match your Regex #"^sly_".
A simpler solution is to provide the search pattern to GetFiles() method like
Directory.GetFiles(#"D:\mypath", "sly_*")

How to GetFiles from multiple different paths

Can we get files from different location using some Built-In Function in C# without any loop. Like if I have following paths
C:\Folder1
C:\abc\Folder2
D:\Folder3
I want to get all files from Folder1, Folder2 and Folder3 at same time without using any loop.
According to MSDN, you can search for files in a single directory.
For example:
Directory.GetFiles("C:\Folder1")
You just need to adapt, however an extension method is not possible since it's a static class.
More info here: http://msdn.microsoft.com/en-us/library/07wt70x2(v=vs.110).aspx
Basiccly, it means that a loop is required to do search for all the paths. Otherwise, it's not possible.
A loop is required there is no built-in function for that.
You can maintain List<string> for the purpose.
Example:
List<string> lstPaths = new List<string>();
lstPaths.Add(#"C:\Folder1");
lstPaths.Add(#"C:\abc\Folder2");
lstPaths.Add(#"D:\Folder3");
foreach(string sPath in lstPaths)
{
string[] arrFiles = Directory.GetFiles(sPath);
//you can loop through arrFiles here
}

converting filenames to lower case

was hoping for some advice as to how to convert existing file names in a folder...all to lower case.
I felt that a good start would be to save the file names in a list and convert them all to lower.
How can I replace the existing file names in the folder to the lower case ones?
List<string> codes = new List<string>();
string[]productCodes = Directory.GetFiles(#"C:\Users\Ariang\Desktop\screenshotslowercase\screenshots");
codes = productCodes.ToList();
codes = codes.ConvertAll(t => t.ToLower());
This should work:
foreach (var file in Directory.GetFiles(#"C:\Temp\testrename"))
{
File.Move(file, file.ToLowerInvariant());
}
A few notes, first of all I have tested this and it works, somebody else mentioned using a temporary variable, but I haven't needed to do this.
Also, I have run this multiple times on the same directory, and I don't get an IOException the second or third time around, so I don't think any additional checking is necessary.
However, I am on Windows 8 and targeting .Net 4.5, things may be different on earlier versions of Windows or .Net.
Windows system doesn't see difference betweeen lower and upper letters in file names. Thats why you can't convert like "MyFile" -> "myfile". Use two steps instead:
foreach (var file in Directory.GetFiles(#"C:\Temp\testrename"))
{
var tempName = "." + file.ToLowerInvariant();
File.Move(file, tempName);
File.Move(tempName, file.ToLowerInvariant());
}
no need for list and all that. Simple read the file name from directory and use
System.IO.File.Move("oldfilename", "oldfilename".ToLower());
string[] files = Directory.GetFiles(dir);
foreach(string file in files)
{
System.IO.File.Move(file, file.ToLowerInvariant());
}

Trim End of a UNC path to a backslash and then add to a list

Let me start of by saying that my C# is pretty bad. I am working on a tool for my job (I hope others could find it useful too) that will create security groups in AD based on a provided file path from an SMB share, assign appropriate permissions on the directory (last folder in the file path), and then give the groups list permissions to each parent folder above it so users can navigate to the directory while not seeing other directories they have not been given access to (we use access based enumeration on our file servers, so they will not see any other folders unless they already have access to them by another security group, etc...). I have the first two parts working. So, right now the program creates the needed security groups in AD and assigns them the correct permissions on the directory for the path provided (\fileserver\some\example\shareddirectory).
What I think I need to do is basically make a list that will contain a path for each of the parent folders for the directory then use a foreach loop to assign the list permissions on the ACLs of each directory. So, using the example above, the list would include the following:
\\fileserver\some
\\fileserver\some\example
The number of parent folders can vary, so the number of items in the list could vary too.
First question: is making a list and then using a foreach loop a good way to do this or is there a better way?
Second question: how would I do this? So far, I have learned that dealing with backslashes is tricky because they are escape characters. I basically got stuck on trimming the string to remove the last backslash and the characters that follow it from the string before adding it to the list and how to do this recursively till there is nothing left to trim.
Thanks!
You can do something like the following
class DirectoryHelper
{
public List<string> GetDirectories(string path)
{
List<string> list = new List<string>();
if (!string.IsNullOrEmpty(path))
{
if (path.Last() != '\\')
{
path += "\\";
}
EnumerateDictories(list, path);
}
return list;
}
private void EnumerateDictories(IList<string> results, string path)
{
var parent = Directory.GetParent(path);
if (parent != null)
{
EnumerateDictories(results, parent.FullName);
results.Add(parent.FullName);
}
}
}
This is called like:
DirectoryHelper helper = new DirectoryHelper();
var dir = helper.GetDirectories(#"C:\Temp\Folder1\");
You have to add some code to protect against invalid directory strings I believe,
but you probably get the idea.
-update-
have edited some backslash handling, so that you do not have to care for it.

file exists by file name pattern

I am using:
File.Exists(filepath)
What I would like to do is swop this out for a pattern, because the first part of the filename changes.
For example: the file could be
01_peach.xml
02_peach.xml
03_peach.xml
How can I check if the file exists based on some kind of search pattern?
You can do a directory list with a pattern to check for files
string[] files = System.IO.Directory.GetFiles(path, "*_peach.xml", System.IO.SearchOption.TopDirectoryOnly);
if (files.Length > 0)
{
//file exist
}
If you're using .net framework 4 or above you could use Directory.EnumerateFiles
bool exist = Directory.EnumerateFiles(path, "*_peach.xml").Any();
This could be more efficient than using Directory.GetFiles since you avoid iterating trough the entire file list.
Get a list of all matching files using System.IO.DirectoryInfo.GetFiles()
Also see SO Questions:
Is there a wildcard expansion option for .net apps?
How do I check if a filename matches a wildcard pattern
and many others...
For more advanced searching against a specific pattern, it might be worth using File Globbing which allows you to use search patterns like you would in a .gitignore file.
See here: https://learn.microsoft.com/en-us/dotnet/core/extensions/file-globbing
This allows you to add both inclusions & exclusions to your search.
Please see below the example code snippet from the Microsoft Source above:
Matcher matcher = new Matcher();
matcher.AddIncludePatterns(new[] { "*_peach.xml" });
IEnumerable<string> matchingFiles = matcher.GetResultsInFullPath(filepath);

Categories