Every example I see seems to be for recursively getting files in subdirectories uses files only. What I'm trying to do is search a folder for a particular subdirectory named "xxx" then save that path to a variable so I can use it for other things.
Is this possible without looping through all the directories and comparing by name?
Well
Directory.GetDirectories(root);
will return you an array of the subdirectories.
You can then use Linq to find the one you're interested in:
IEnumerable<string> list = Directory.GetDirectories(root).Where(s => s.Equals("test"));
which isn't a loop in your code, but is still a loop nevertheless. So the ultimate answer is that "no you can't find a folder 'test' without looping".
You could add .SingleOrDefault() to the Linq, but that would depend on what you wanted to do if your "test" folder couldn't be found.
If you change the GetDirectories call to include the SearchOption SearchOption.AllDirectories then it will do the recursion for you as well. This version supports searching - you have to supply a search string - though in .NET Framework it's case sensitive searching. To return all sub directories you pass "*" as the search term.
Obviously in this case the call could return more than one item if there was more than one folder named "test" in your directory tree.
var foldersFound = Directory.GetDirectories(root, "test", SearchOption.AllDirectories)
This will return a string array with all the folders found with the given name. You can change the last parameter so that it only checks top level directories and you can change root to adjust where it is starting from.
First of all, "No, it is not possible without looping through all the directories and comparing by name".
I believe your real question is "Is there an existing API which will handle looping through all the directories and comparing by name for me?"
Yes, there is. It's called Directory.Exists():
var xxxPath = Path.Combine(parentFolder, "xxx");
if (Directory.Exists(xxxPath))
savedPath = xxxPath;
Yes, I believe that the only available solution (short of third party libraries) is a recursive search for the directory via name comparison.
You can use Windows Search which provides api for .Net too. Here is more detailed information: Windows Search 4.0 for Developers
Here is a snippet for searching for a folder using two filters while considering for the UnauthorizedAccessException, it can be refactored to use only one filter:
public static string FindGitPath(string firstFilter, string secondFilter, string initialPath)
{
string gitPath = string.Empty;
foreach (var i in Directory.GetDirectories(initialPath)) {
try {
foreach (var f in Directory.GetDirectories(i, firstFilter, SearchOption.AllDirectories)) {
foreach (var s in Directory.GetDirectories(f)) {
if (s == Path.Combine(f,secondFilter)) {
gitPath = f;
break;
}
}
}
} catch (UnauthorizedAccessException) {
Console.WriteLine("Path is not accessible: {0}", i);
}
}
return gitPath;
}
Usage example:
Console.WriteLine("Retrieved the git database folder as {0}", FindGitPath("database",".git", "c:\\"));
Related
I was successfully able to remove read only attribute on a file using the following code snippet:
In main.cs
FileSystemInfo[] sqlParentFileSystemInfo = dirInfo.GetFileSystemInfos();
foreach (var childFolderOrFile in sqlParentFileSystemInfo)
{
RemoveReadOnlyFlag(childFolderOrFile);
}
private static void RemoveReadOnlyFlag(FileSystemInfo fileSystemInfo)
{
fileSystemInfo.Attributes = FileAttributes.Normal;
var di = fileSystemInfo as DirectoryInfo;
if (di != null)
{
foreach (var dirInfo in di.GetFileSystemInfos())
RemoveReadOnlyFlag(dirInfo);
}
}
Unfortunately, this doesn't work on the folders. After running the code, when I go to the folder, right click and do properties, here's what I see:
The read only flag is still checked although it removed it from files underneath it. This causes a process to fail deleting this folder. When I manually remove the flag and rerun the process (a bat file), it's able to delete the file (so I know this is not an issue with the bat file)
How do I remove this flag in C#?
You could also do something like the following to recursively clear readonly (and archive, etc.) for all directories and files within a specified parent directory:
private void ClearReadOnly(DirectoryInfo parentDirectory)
{
if(parentDirectory != null)
{
parentDirectory.Attributes = FileAttributes.Normal;
foreach (FileInfo fi in parentDirectory.GetFiles())
{
fi.Attributes = FileAttributes.Normal;
}
foreach (DirectoryInfo di in parentDirectory.GetDirectories())
{
ClearReadOnly(di);
}
}
}
You can therefore call this like so:
public void Main()
{
DirectoryInfo parentDirectoryInfo = new DirectoryInfo(#"c:\test");
ClearReadOnly(parentDirectoryInfo);
}
Try DirectoryInfo instead of FileInfo
DirectoryInfo di = new DirectoryInfo(#"c:\temp\content");
di.Attributes = FileAttributes.Normal;
To clean up attrbutes on files-
foreach (string fileName in System.IO.Directory.GetFiles(#"c:\temp\content"))
{
System.IO.FileInfo fileInfo = new System.IO.FileInfo(fileName);
fileInfo.Attributes = FileAttributes.Normal;
}
The dialog just works in a fairly bizarre way. It always shows up the way you see it in your screen shot, whatever the state of the ReadOnly attribute. The checkbox is in the 'indetermined' state. You have to click it and either clear or check it to make it perform its action. And in spite of the prompt text (but not the hint next to the checkbox), it only changes the ReadOnly attribute on the files in the directory, not the directory itself.
Use the attrib command line command to see what is really going on. In all likelihood, your code fails because the directory contains files that have their ReadOnly attribute set. You'll have to iterate them.
The read-only flag on directories in Windows is actually a misnomer. The folder does not use the read-only flag. The issue is going to be with the customization. The flag is used by Windows to identify that there are customizations on the folder.
This is an old post, with an issue that is sunsetting, but, figured people might still run into it, as it is pretty annoying when you hit it.
Microsoft's Explanation
IEnumerable / Lambda solution for recursively removing readonly attribute from directories and files:
new DirectoryInfo(#"some\test\path").GetDirectories("*", SearchOption.AllDirectories).ToList().ForEach(
di => {
di.Attributes &= ~FileAttributes.ReadOnly;
di.GetFiles("*", SearchOption.TopDirectoryOnly).ToList().ForEach(fi => fi.IsReadOnly = false);
}
);
Set the Attributes property on the original dirInfo:
dirInfo.Attributes = FileAttributes.Normal;
FileSystemInfo[] sqlParentFileSystemInfo = dirInfo.GetFileSystemInfos();
foreach (var childFolderOrFile in sqlParentFileSystemInfo)
{
RemoveReadOnlyFlag(childFolderOrFile);
}
Just in case any one happens across this later...
ALL of the other answers posted before mine are either wrong or use unnecessary recursion.
First of all the "Read Only" check box in the property dialog of windows always has the tri-state marker for folders. This is because the folder itself is not read only but the files inside can be.
If you want to set/unset read only flag for ALL files. you can do it simply as follows:
void SetReadOnlyFlagForAllFiles(DirectoryInfo directory, bool isReadOnly)
{
// Iterate over ALL files using "*" wildcard and choosing to search all directories.
foreach(FileInfo File in directory.GetFiles("*", SearchOption.All.Directories))
{
// Set flag.
File.IsReadOnly = isReadOnly;
}
}
I see that #DotnetDude said in comments that solutions of guys don't work. To my mind it is happens because guys don't mentioned that need to use File.SetAttributes method to apply new attributes.
This may or may not be directly related, but the root issue in your case may be caused by the underlying files. For example, I ran into this issue trying to delete a directory:
System.IO.Directory.Delete(someDirectory, true)
This results in "Access to the path 'blah' is denied". To resolve this underlying problem, I removed the read-only attribute on sub-files and was then able to remove the parent directory. In my case, I was using powershell, so you can use the .NET equivalent.
dir -r $PrePackageDirectory |% {if ($_.PSIsContainer -ne $true){$_.IsReadOnly = $false}}
Shell("net share sharefolder=c:\sharefolder/GRANT:Everyone,FULL")
Shell("net share sharefolder= c:\sharefolder/G:Everyone:F /SPEC B")
Shell("Icacls C:\sharefolder/grant Everyone:F /inheritance:e /T")
Shell("attrib -r +s C:\\sharefolder\*.* /s /d", AppWinStyle.Hide)
this code is working for me.. to share a folder to every one with read and write permission
I have quotas-enabled drive and I want to remove all files created by specifed user (actually a set of applications that runs using special account) from that drive. How can I do this without recursivly checking all files and folders on HDD is it created by specifed user or not? I just need to get "iterator".
Take a look on following example
[Test]
public void Test()
{
string user = #"Domain\UserName";
var files = Directory.EnumerateFiles(#"C:\TestFolder")
.Where(x => IsOwner(x, user));
Parallel.ForEach(files, File.Delete);
}
private static bool IsOwner(string filePath, string user)
{
return string.Equals(File.GetAccessControl(filePath).GetOwner(typeof (NTAccount)).Value, user,
StringComparison.OrdinalIgnoreCase);
}
In term of improving performance, I think you could use Task Parallel Library when using recursive algorithm to search file and folder.
Another way, you could do that Lucence was a useful framework for search and it was already published version for .NET
Actually, you can do that iteratively and very efficiently using USN Change Journal, see http://msdn.microsoft.com/en-us/library/windows/desktop/aa363798.aspx. With proper use of filtering, you can get list of files created by specific user within specific time period.
On the other hand, this technique is quite complicated and is suitable for time-critical applications; if efficiency is not the focal point of your application, I'd choose simpler solution.
I have what I think is probably quite a simple question to answer (for someone). Given a path, say C:\A\B\C\D\, what is the most efficient, compact and elegant way to re-create that path on the system, assuming it doesn't already exist (C:\A\B might, of course), in c#?
The only thing I can think of involves a rather grubby looking little path parser, with all of the potential pit-falls this entails. I'm sure many people here will have encountered this little problem before and have a better solution than I can come up with.
So, your thoughts please gentlemen (and ladies)?
System.IO.Directory.CreateDirectory(#"C:\A\B\C\D\") will do just fine.
MSDN example
// Specify the directories you want to manipulate.
DirectoryInfo di = new DirectoryInfo(#"c:\MyDir");
try
{
// Determine whether the directory exists.
if (di.Exists)
{
// Indicate that it already exists.
Console.WriteLine("That path exists already.");
return;
}
// Try to create the directory.
di.Create();
Console.WriteLine("The directory was created successfully.");
}
catch (Exception e)
{
Console.WriteLine("The process failed: {0}", e.ToString());
}
finally {}
Directory.CreateDirectory(#"C:\A\B\C\D\");
Creates all directories in the path if they don't already exist, and even returns you a DirectoryInfo object of the child directory (D, in this case).
You might want to have a look into MSDN and the ´Directory.exists´-Method.
This way you can detect if "C:\A\B\C\D..." already exists and if not you can create it with Directory.Create(String directoryName)...
Directory.CreateDirectory will take care of that for you.
string folderPath = "C:\A\B\C\D"
if (!System.IO.Directory.Exists(folderPath))
System.IO.Directory.CreateDirectory(folderPath);
I know that in the same directory where my code is being executed some files are located. I need to find them and pass to another method:
MyLib.dll
Target1.dll
Target2.dll
Foo(new[] { "..\\..\\Target1.dll", "..\\..\\Target2.dll" });
So I call System.IO.Directory.GetFiles(path, "*.dll"). But now I need to get know the path:
string path = new FileInfo((Assembly.GetExecutingAssembly().Location)).Directory.FullName)
but is there more short way?
You may try the Environment.CurrentDirectory property. Note that depending on the type of application (Console, WinForms, ASP.NET, Windows Service, ...) and the way it is run this might behave differently.
Environment.CurrentDirectory returns the current directory, not the directory where the executed code is located. If you use Directory.SetCurrentDirectory, or if you start the program using a shortcut where the directory is set this won't be the directory you are looking for.
Stick to your original solution. Hide the implementation (and make it shorter) using a property:
private DirectoryInfo ExecutingFolder
{
get
{
return new DirectoryInfo (
System.IO.Path.GetDirectoryName (
System.Reflection.Assembly.GetExecutingAssembly().Location));
}
}
I am creating a TFS tool that will get "changeset information" from the TFS server.
Now, I want to provide a "TFS Browser" so that the user can browse what "branch/folder" he wants to fetch information from.
I am using a TreeView control and the GetItems function to get the items' path from TFS:
private void treeView1_BeforeExpand(object sender, TreeViewCancelEventArgs e)
{
e.Node.Nodes.RemoveAt(0);
RecursionType recursion = RecursionType.OneLevel;
Item[] items = null;
// Get the latest version of the information for the items.
ItemSet itemSet = sourceControl.GetItems(e.Node.Tag.ToString(), recursion);
items = itemSet.Items;
foreach (Item item in items)
{
if (item.ServerItem == e.Node.Tag.ToString()) //Skip self
continue;
string filename = Path.GetFileName(item.ServerItem);
if (Path.GetExtension(filename) == "")
{
TreeNode node = new TreeNode(filename, new TreeNode[] { new TreeNode() });
node.Tag = item.ServerItem;
e.Node.Nodes.Add(node);
}
}
}
The code below demonstrates that after clicking the "expand" button from a node, the app will "query" the items that are below the current "branch" (e).
However, I don't want to include files to the browser. As a quick and dirty check, I am checking if the "path" has an extension and if not, assume that it is a directory and show it. All was good until I discovered that we have a folder named "v1.1".
There is a solution. I can re-invoke GetItems and check its content. According to MSDN:
If the path argument is a file,
returns a set of Items that contain
just that file. If the path is a
folder, returns a set of Items that
contain all items in that folder. If
the path contains a wildcard
character, returns a set of Items in
the specified folder that match the
wildcard.
However, each call to GetItems take roughly a second and if a folder contains multiple files, the "expansion" of the node takes forever.
So, is there a way to just get all the "folders" from TFS? Or any other idea how to check if a path is a folder or a file?
Thanks!
It seems that there is a member called .ItemType for Item. You can check against that.
One solution that I have just found is to use the GetFileTypes method to retrieve the different extensions registered on the server. Then check every "item" against these extensions like so:
if (!Extensions.Contains(Path.GetExtension(item.ServerItem).Replace(".","").ToLower()))
{
//Add Node
}
However, this is not really fool proof. What if a folder is named FOLDER.DLL?