How to recursively list all the files in a directory and child directories in C#?
-
7The problem with this is that it breaks very easily if you don't have access to a single directory: no results... – Marc Gravell May 30 '09 at 09:15
-
3If you run into trouble when some files aren't accessible, look into [Enumerating Files Throwing Exception](http://stackoverflow.com/questions/7756626/enumerating-files-throwing-exception) – CodesInChaos Aug 08 '13 at 11:05
24 Answers
Note that in .NET 4.0 there are (supposedly) iterator-based (rather than array-based) file functions built in:
foreach (string file in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories))
{
Console.WriteLine(file);
}
At the moment I'd use something like below; the inbuilt recursive method breaks too easily if you don't have access to a single sub-dir...; the Queue<string>
usage avoids too much call-stack recursion, and the iterator block avoids us having a huge array.
static void Main() {
foreach (string file in GetFiles(SOME_PATH)) {
Console.WriteLine(file);
}
}
static IEnumerable<string> GetFiles(string path) {
Queue<string> queue = new Queue<string>();
queue.Enqueue(path);
while (queue.Count > 0) {
path = queue.Dequeue();
try {
foreach (string subDir in Directory.GetDirectories(path)) {
queue.Enqueue(subDir);
}
}
catch(Exception ex) {
Console.Error.WriteLine(ex);
}
string[] files = null;
try {
files = Directory.GetFiles(path);
}
catch (Exception ex) {
Console.Error.WriteLine(ex);
}
if (files != null) {
for(int i = 0 ; i < files.Length ; i++) {
yield return files[i];
}
}
}
}

- 53,146
- 19
- 236
- 237

- 1,026,079
- 266
- 2,566
- 2,900
-
Take into account whitespaces as directory name => http://stackoverflow.com/q/5368054/2336304 – SerG Feb 20 '16 at 10:18
-
12For all who want to know whether `*.*` also includes files *without* file extension: Yes, it does, tested a minute ago. – Tobias Knauss Jun 09 '16 at 16:28
This article covers all you need. Except as opposed to searching the files and comparing names, just print out the names.
It can be modified like so:
static void DirSearch(string sDir)
{
try
{
foreach (string d in Directory.GetDirectories(sDir))
{
foreach (string f in Directory.GetFiles(d))
{
Console.WriteLine(f);
}
DirSearch(d);
}
}
catch (System.Exception excpt)
{
Console.WriteLine(excpt.Message);
}
}
Added by barlop
GONeale mentions that the above doesn't list the files in the current directory and suggests putting the file listing part outside the part that gets directories. The following would do that. It also includes a Writeline line that you can uncomment, that helps to trace where you are in the recursion that may help to show the calls to help show how the recursion works.
DirSearch_ex3("c:\\aaa");
static void DirSearch_ex3(string sDir)
{
//Console.WriteLine("DirSearch..(" + sDir + ")");
try
{
Console.WriteLine(sDir);
foreach (string f in Directory.GetFiles(sDir))
{
Console.WriteLine(f);
}
foreach (string d in Directory.GetDirectories(sDir))
{
DirSearch_ex3(d);
}
}
catch (System.Exception excpt)
{
Console.WriteLine(excpt.Message);
}
}
-
88This method does not list files for the initial directory, only it's sub dirs and lower. I would move GetFiles outside GetDirectories – GONeale Jul 06 '10 at 00:35
-
1Sometimes one doesn't want the files for the initial directory, in which case this is perfect for reasonably small structures. For very large lists, use something like Marc Gravell's solution: http://stackoverflow.com/a/929418/91189 – Joseph Gabriel Nov 25 '14 at 14:19
-
2@GONeale is correct. It is much less plausible for a user not to expect the file listing of the input root directory. The word input is key here. It has been input for a reason. – Florin Mircea Jul 07 '16 at 18:08
-
2I had to add a try catch around the inner foreach loop otherwise it does not continue of access denied errors – Shaun Vermaak Feb 27 '17 at 15:26
-
3You should avoid catching Exception - would you really want to catch An OutOfMemoryException for example? Only catch what you can handle. – alastairtree Mar 05 '18 at 14:25
-
@GONeale can you edit the answer and add an example with that so it lists the initial directory? – barlop May 14 '20 at 10:03
-
@GONeale While i'm not the great JohnT, I've just edited the answer to add such an example based on your suggestion – barlop May 14 '20 at 19:56
Directory.GetFiles("C:\\", "*.*", SearchOption.AllDirectories)

- 2,044
- 1
- 12
- 5
-
2How to avoid the error if login user doesn't have access on some of the folders. – Romil Kumar Jain Feb 10 '15 at 11:37
-
6@Romil I do not believe this code snippet is trying to indicate full functionality, only the raw functionality that the OP was seeking. Thanks for sharing, Pescuma! – kayleeFrye_onDeck Mar 10 '15 at 14:15
-
@kayleeFrye_onDeck, I put only a concern in case if there is an raised for any of the folder while getting files. Due to this concern we implement our custom recursive function. – Romil Kumar Jain Mar 11 '15 at 04:15
-
4You will receive "UnauthorizedAccessException" with this solution. You should have a solution that can handle errors like this. – Kairan Dec 16 '17 at 23:25
-
1For all dirs `Directory.GetDirectories(@"yourpath" , "*.*", SearchOption.AllDirectories)` – Soner from The Ottoman Empire Dec 29 '20 at 14:49
Shortest record
string[] files = Directory.GetFiles(@"your_path", "*.jpg", SearchOption.AllDirectories);

- 3,903
- 8
- 35
- 46

- 261
- 3
- 3
-
4
-
2I don't understand why this answer was added in the first place and why it has so many upvotes. It's the same exact answer as https://stackoverflow.com/a/23253890/1039753 that was added 6 years before this answer. Why would you just not improve on the old answer if you wanted to add a string declaration and @ to avoid needing to escape the slashes? – Arvo Bowen Feb 10 '21 at 04:32
In .NET 4.5, at least, there's this version that is much shorter and has the added bonus of evaluating any file criteria for inclusion in the list:
public static IEnumerable<string> GetAllFiles(string path,
Func<FileInfo, bool> checkFile = null)
{
string mask = Path.GetFileName(path);
if (string.IsNullOrEmpty(mask)) mask = "*.*";
path = Path.GetDirectoryName(path);
string[] files = Directory.GetFiles(path, mask, SearchOption.AllDirectories);
foreach (string file in files)
{
if (checkFile == null || checkFile(new FileInfo(file)))
yield return file;
}
}
Use like this:
var list = GetAllFiles(mask, (info) => Path.GetExtension(info.Name) == ".html").ToList();

- 30,350
- 66
- 462
- 664

- 2,509
- 1
- 33
- 40
-
This doesn't handle a case where you have an empty directory... there is no return statement inside the function. – FrumkinWY Aug 06 '18 at 14:53
-
@FrumkinWY what happens with an empty directory? I don't have a machine handy to test this on now. – John Kaster Aug 07 '18 at 19:34
IEnumerable<string> GetFilesFromDir(string dir) =>
Directory.EnumerateFiles(dir).Concat(
Directory.EnumerateDirectories(dir)
.SelectMany(subdir => GetFilesFromDir(subdir)));

- 2,877
- 1
- 26
- 29
This is how we can get files as FileInfo of its child directories,
var dir = new DirectoryInfo(rootPath);
FileInfo[] files = dir.GetFiles("*.*", SearchOption.AllDirectories);

- 391
- 4
- 7
Some excellent answers but these answers did not solve my issue.
As soon as a folder permission issue arises: "Permission Denied" the code fails. This is what I used to get around the "Permission Denied" issue:
private int counter = 0;
private string[] MyDirectories = Directory.GetDirectories("C:\\");
private void ScanButton_Click(object sender, EventArgs e)
{
Thread MonitorSpeech = new Thread(() => ScanFiles());
MonitorSpeech.Start();
}
private void ScanFiles()
{
string CurrentDirectory = string.Empty;
while (counter < MyDirectories.Length)
{
try
{
GetDirectories();
CurrentDirectory = MyDirectories[counter++];
}
catch
{
if (!this.IsDisposed)
{
listBox1.Invoke((MethodInvoker)delegate { listBox1.Items.Add("Access Denied to : " + CurrentDirectory); });
}
}
}
}
private void GetDirectories()
{
foreach (string directory in MyDirectories)
{
GetFiles(directory);
}
}
private void GetFiles(string directory)
{
try
{
foreach (string file in Directory.GetFiles(directory, "*"))
{
listBox1.Invoke((MethodInvoker)delegate { listBox1.Items.Add(file); });
}
}
catch
{
listBox1.Invoke((MethodInvoker)delegate { listBox1.Items.Add("Access Denied to : " + directory); });
}
}
Hope this helps others.

- 51,870
- 39
- 111
- 135

- 2,692
- 3
- 34
- 55
In Framework 2.0 you can use (It list files of root folder, it's best the most popular answer):
static void DirSearch(string dir)
{
try
{
foreach (string f in Directory.GetFiles(dir))
Console.WriteLine(f);
foreach (string d in Directory.GetDirectories(dir))
{
Console.WriteLine(d);
DirSearch(d);
}
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message);
}
}

- 1,977
- 1
- 21
- 32
A simple and clean solution
/// <summary>
/// Scans a folder and all of its subfolders recursively, and updates the List of files
/// </summary>
/// <param name="sFullPath">Full path of the folder</param>
/// <param name="files">The list, where the output is expected</param>
internal static void EnumerateFiles(string sFullPath, List<FileInfo> fileInfoList)
{
try
{
DirectoryInfo di = new DirectoryInfo(sFullPath);
FileInfo[] files = di.GetFiles();
foreach (FileInfo file in files)
fileInfoList.Add(file);
//Scan recursively
DirectoryInfo[] dirs = di.GetDirectories();
if (dirs == null || dirs.Length < 1)
return;
foreach (DirectoryInfo dir in dirs)
EnumerateFiles(dir.FullName, fileInfoList);
}
catch (Exception ex)
{
Logger.Write("Exception in Helper.EnumerateFiles", ex);
}
}

- 11,464
- 3
- 53
- 79

- 8,435
- 1
- 22
- 20
-
4You're manually doing what DirectoryInfo.GetFiles() will do for you out of the box - just use the overload with SearchOption.AllDirectories and it will recurse all on its own. So that's a *complicated* solution. – philw Nov 08 '16 at 14:19
I prefer to use DirectoryInfo because I can get FileInfo's, not just strings.
string baseFolder = @"C:\temp";
DirectoryInfo di = new DirectoryInfo(baseFolder);
string searchPattern = "*.xml";
ICollection<FileInfo> matchingFileInfos = di.GetFiles(searchPattern, SearchOption.AllDirectories)
.Select(x => x)
.ToList();
I do this in case in the future I need future filtering..based on the properties of FileInfo.
string baseFolder = @"C:\temp";
DirectoryInfo di = new DirectoryInfo(baseFolder);
string searchPattern = "*.xml";
ICollection<FileInfo> matchingFileInfos = di.GetFiles(searchPattern, SearchOption.AllDirectories)
.Where(x => x.LastWriteTimeUtc < DateTimeOffset.Now)
.Select(x => x)
.ToList();
I can also resort back to strings if need be. (and still am future proofed for filters/where-clause stuff.
string baseFolder = @"C:\temp";
DirectoryInfo di = new DirectoryInfo(baseFolder);
string searchPattern = "*.xml";
ICollection<string> matchingFileNames = di.GetFiles(searchPattern, SearchOption.AllDirectories)
.Select(x => x.FullName)
.ToList();
Note that "." is a valid search pattern if you want to filer by extension.

- 26,328
- 10
- 113
- 146
private void GetFiles(DirectoryInfo dir, ref List<FileInfo> files)
{
try
{
files.AddRange(dir.GetFiles());
DirectoryInfo[] dirs = dir.GetDirectories();
foreach (var d in dirs)
{
GetFiles(d, ref files);
}
}
catch (Exception e)
{
}
}

- 21
- 1
- 5
-
1
-
@MassimilianoKraus I'd argue that, while not required, it makes it clearer that his method will alter `files` and you can't just give `new List
()` as parameter anymore which would be useless. Might allow for some sub-optimisation and avoid creating a new object unless required. – jeromej Apr 07 '20 at 07:25 -
@JeromeJ if you know what OOP is, you know that whenever you pass an object to a method, that method can change the object's properties/fields. So `ref` doesn't make anything clearer. The `ref`'s purpose is to change the whole `files` pointer even for the method's caller: it's a dangerous operation and here there is no need for that: you can just fill the List, you don't need to re-point it to another List on the heap. `ref` should be used only in very particular cases; most of the time you just need to implement things in a more functional-paradigm way. – Massimiliano Kraus Apr 08 '20 at 08:40
var files = Directory.EnumerateFiles(@"D:\PATH", "*.*",SearchOption.AllDirectories).Select(i=>new FileInfo(i));
foreach(var fl in files)
Console.WriteLine(fl.FullName);
Short and simple solution if you want to filter by the last update date
string dir = @"D:\PATH";
DateTime from_date = DateTime.Now.Date;
DateTime to_date = DateTime.Now.Date.AddHours(23);
var files = Directory.EnumerateFiles(dir, "*.*",SearchOption.AllDirectories).Select(i=>new FileInfo(i))
.Where(file=>file.LastWriteTime >= from_date && file.LastWriteTime <= to_date);
foreach(var fl in files)
Console.WriteLine(fl.FullName);

- 7,278
- 9
- 52
- 98
-
This probably won’t be a problem for many uses, but just mentioning that according to the documentation SearchOption.AllDirectories “includes reparse points such as mounted drives and symbolic links in the search” and “If you choose AllDirectories in your search and the directory structure contains a link that creates a loop, the search operation enters an infinite loop.” Not sure which of the other methods this might or might not apply to. – Sven Viking Mar 08 '23 at 09:07
This one helped me to get all files in a directory and sub directories, May be helpful for someone. [ Inspired from above answers ]
static void Main(string[] args)
{
try
{
var root = @"G:\logs";
DirectorySearch(root);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
public static void DirectorySearch(string root, bool isRootItrated = false)
{
if (!isRootItrated)
{
var rootDirectoryFiles = Directory.GetFiles(root);
foreach (var file in rootDirectoryFiles)
{
Console.WriteLine(file);
}
}
var subDirectories = Directory.GetDirectories(root);
if (subDirectories?.Any() == true)
{
foreach (var directory in subDirectories)
{
var files = Directory.GetFiles(directory);
foreach (var file in files)
{
Console.WriteLine(file);
}
DirectorySearch(directory, true);
}
}
}

- 2,415
- 18
- 30
To avoid the UnauthorizedAccessException
, I use:
var files = GetFiles(@"C:\", "*.*", SearchOption.AllDirectories);
foreach (var file in files)
{
Console.WriteLine($"{file}");
}
public static IEnumerable<string> GetFiles(string path, string searchPattern, SearchOption searchOption)
{
var foldersToProcess = new List<string>()
{
path
};
while (foldersToProcess.Count > 0)
{
string folder = foldersToProcess[0];
foldersToProcess.RemoveAt(0);
if (searchOption.HasFlag(SearchOption.AllDirectories))
{
//get subfolders
try
{
var subfolders = Directory.GetDirectories(folder);
foldersToProcess.AddRange(subfolders);
}
catch (Exception ex)
{
//log if you're interested
}
}
//get files
var files = new List<string>();
try
{
files = Directory.GetFiles(folder, searchPattern, SearchOption.TopDirectoryOnly).ToList();
}
catch (Exception ex)
{
//log if you're interested
}
foreach (var file in files)
{
yield return file;
}
}
}

- 7,027
- 11
- 57
- 81
If you only need filenames and since I didn't really like most of the solutions here (feature-wise or readability-wise), how about this lazy one?
private void Foo()
{
var files = GetAllFiles("pathToADirectory");
foreach (string file in files)
{
// Use can use Path.GetFileName() or similar to extract just the filename if needed
// You can break early and it won't still browse your whole disk since it's a lazy one
}
}
/// <exception cref="T:System.IO.DirectoryNotFoundException">The specified path is invalid (for example, it is on an unmapped drive).</exception>
/// <exception cref="T:System.UnauthorizedAccessException">The caller does not have the required permission.</exception>
/// <exception cref="T:System.IO.IOException"><paramref name="path" /> is a file name.-or-A network error has occurred.</exception>
/// <exception cref="T:System.IO.PathTooLongException">The specified path, file name, or both exceed the system-defined maximum length. For example, on Windows-based platforms, paths must be less than 248 characters and file names must be less than 260 characters.</exception>
/// <exception cref="T:System.ArgumentNullException"><paramref name="path" /> is null.</exception>
/// <exception cref="T:System.ArgumentException"><paramref name="path" /> is a zero-length string, contains only white space, or contains one or more invalid characters as defined by <see cref="F:System.IO.Path.InvalidPathChars" />.</exception>
[NotNull]
public static IEnumerable<string> GetAllFiles([NotNull] string directory)
{
foreach (string file in Directory.GetFiles(directory))
{
yield return file; // includes the path
}
foreach (string subDir in Directory.GetDirectories(directory))
{
foreach (string subFile in GetAllFiles(subDir))
{
yield return subFile;
}
}
}

- 10,508
- 2
- 43
- 62
Some improved version with max lvl to go down in directory and option to exclude folders:
using System;
using System.IO;
class MainClass {
public static void Main (string[] args) {
var dir = @"C:\directory\to\print";
PrintDirectoryTree(dir, 2, new string[] {"folder3"});
}
public static void PrintDirectoryTree(string directory, int lvl, string[] excludedFolders = null, string lvlSeperator = "")
{
excludedFolders = excludedFolders ?? new string[0];
foreach (string f in Directory.GetFiles(directory))
{
Console.WriteLine(lvlSeperator+Path.GetFileName(f));
}
foreach (string d in Directory.GetDirectories(directory))
{
Console.WriteLine(lvlSeperator + "-" + Path.GetFileName(d));
if(lvl > 0 && Array.IndexOf(excludedFolders, Path.GetFileName(d)) < 0)
{
PrintDirectoryTree(d, lvl-1, excludedFolders, lvlSeperator+" ");
}
}
}
}
input directory:
-folder1
file1.txt
-folder2
file2.txt
-folder5
file6.txt
-folder3
file3.txt
-folder4
file4.txt
file5.txt
output of the function (content of folder5 is excluded due to lvl limit and content of folder3 is excluded because it is in excludedFolders array):
-folder1
file1.txt
-folder2
file2.txt
-folder5
-folder3
-folder4
file4.txt
file5.txt

- 1,138
- 11
- 28
Here's my angle on it, based on Hernaldo's, if you need to find files with names of a certain pattern, such as XML files that somewhere in their name contain a particular string:
// call this like so: GetXMLFiles("Platypus", "C:\\");
public static List<string> GetXMLFiles(string fileType, string dir)
{
string dirName = dir;
var fileNames = new List<String>();
try
{
foreach (string f in Directory.GetFiles(dirName))
{
if ((f.Contains(fileType)) && (f.Contains(".XML")))
{
fileNames.Add(f);
}
}
foreach (string d in Directory.GetDirectories(dirName))
{
GetXMLFiles(fileType, d);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return fileNames;
}

- 8,547
- 144
- 472
- 862
Listing files and folders to model, custom implementation.
This creates a full listing of all files and folders starting from your start directory.
public class DirOrFileModel
{
#region Private Members
private string _name;
private string _location;
private EntryType _entryType;
#endregion
#region Bindings
public string Name
{
get { return _name; }
set
{
if (value == _name) return;
_name = value;
}
}
public string Location
{
get { return _location; }
set
{
if (value == _location) return;
_location = value;
}
}
public EntryType EntryType
{
get { return _entryType; }
set
{
if (value == _entryType) return;
_entryType = value;
}
}
public ObservableCollection<DirOrFileModel> Entries { get; set; }
#endregion
#region Constructor
public DirOrFileModel()
{
Entries = new ObservableCollection<DirOrFileModel>();
}
#endregion
}
public enum EntryType
{
Directory = 0,
File = 1
}
Method:
static DirOrFileModel DirSearch(DirOrFileModel startDir)
{
var currentDir = startDir;
try
{
foreach (string d in Directory.GetDirectories(currentDir.Location))
{
var newDir = new DirOrFileModel
{
EntryType = EntryType.Directory,
Location = d,
Name = Path.GetFileName(d)
};
currentDir.Entries.Add(newDir);
DirSearch(newDir);
}
foreach (string f in Directory.GetFiles(currentDir.Location))
{
var newFile = new DirOrFileModel
{
EntryType = EntryType.File,
Location = f,
Name = Path.GetFileNameWithoutExtension(f)
};
currentDir.Entries.Add(newFile);
}
}
catch (Exception excpt)
{
Console.WriteLine(excpt.Message);
}
return startDir;
}
Usage:
var dir = new DirOrFileModel
{
Name = "C",
Location = @"C:\",
EntryType = EntryType.Directory
};
dir = DirSearch(dir);

- 345
- 3
- 5
If you don't need an iterator-based method, just mentioning there's a built-in recursive option that makes things simpler than the other answers using System.IO.DirectoryInfo.GetFiles
:
var options = new EnumerationOptions();
options.RecurseSubdirectories = true;
var files = new DirectoryInfo(path).GetFiles("*", options);
foreach( var file in files )
Console.WriteLine(file.FullName);
(Where path
is your desired path.)

- 2,660
- 21
- 34
Here is a version of B. Clay Shannon's code not static for excel-files:
class ExcelSearcher
{
private List<string> _fileNames;
public ExcelSearcher(List<string> filenames)
{
_fileNames = filenames;
}
public List<string> GetExcelFiles(string dir, List<string> filenames = null)
{
string dirName = dir;
var dirNames = new List<string>();
if (filenames != null)
{
_fileNames.Concat(filenames);
}
try
{
foreach (string f in Directory.GetFiles(dirName))
{
if (f.ToLower().EndsWith(".xls") || f.ToLower().EndsWith(".xlsx"))
{
_fileNames.Add(f);
}
}
dirNames = Directory.GetDirectories(dirName).ToList();
foreach (string d in dirNames)
{
GetExcelFiles(d, _fileNames);
}
}
catch (Exception ex)
{
//Bam
}
return _fileNames;
}

- 170,088
- 45
- 397
- 571

- 767
- 1
- 10
- 25
var d = new DirectoryInfo(@"C:\logs");
var list = d.GetFiles("*.txt").Select(m => m.Name).ToList();

- 4,384
- 2
- 36
- 53
A very simple solution, returns a list of files.
public static List<string> AllFilesInFolder(string folder)
{
var result = new List<string>();
foreach (string f in Directory.GetFiles(folder))
{
result.Add(f);
}
foreach (string d in Directory.GetDirectories(folder))
{
result.AddRange(AllFilesInFolder(d));
}
return result;
}

- 87
- 7
static void Main(string[] args)
{
string[] array1 = Directory.GetFiles(@"D:\");
string[] array2 = System.IO.Directory.GetDirectories(@"D:\");
Console.WriteLine("--- Files: ---");
foreach (string name in array1)
{
Console.WriteLine(name);
}
foreach (string name in array2)
{
Console.WriteLine(name);
}
Console.ReadLine();
}