404

How to recursively list all the files in a directory and child directories in C#?

Jim McKeeth
  • 38,225
  • 23
  • 120
  • 194
  • 7
    The 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
  • 3
    If 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 Answers24

534

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];
            }
        }
    }
}
T.Todua
  • 53,146
  • 19
  • 236
  • 237
Marc Gravell
  • 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
  • 12
    For 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
229

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);
                }
            }
barlop
  • 12,887
  • 8
  • 80
  • 109
John T
  • 23,735
  • 11
  • 56
  • 82
  • 88
    This 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
  • 1
    Sometimes 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
  • 2
    I 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
  • 3
    You 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
161
Directory.GetFiles("C:\\", "*.*", SearchOption.AllDirectories)
Pescuma
  • 2,044
  • 1
  • 12
  • 5
25

Shortest record

string[] files = Directory.GetFiles(@"your_path", "*.jpg", SearchOption.AllDirectories);
TGnat
  • 3,903
  • 8
  • 35
  • 46
  • 4
    Would that not be `string[] files`? – Philipp Lenssen Aug 13 '20 at 06:47
  • 2
    I 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
15

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();
KyleMit
  • 30,350
  • 66
  • 462
  • 664
John Kaster
  • 2,509
  • 1
  • 33
  • 40
14
IEnumerable<string> GetFilesFromDir(string dir) =>
 Directory.EnumerateFiles(dir).Concat(
 Directory.EnumerateDirectories(dir)
          .SelectMany(subdir => GetFilesFromDir(subdir)));
Rezo Megrelidze
  • 2,877
  • 1
  • 26
  • 29
5

This is how we can get files as FileInfo of its child directories,

var dir = new DirectoryInfo(rootPath);
FileInfo[] files = dir.GetFiles("*.*", SearchOption.AllDirectories);
3

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.

László Papp
  • 51,870
  • 39
  • 111
  • 135
Rusty Nail
  • 2,692
  • 3
  • 34
  • 55
3

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);
    }
}
Hernaldo Gonzalez
  • 1,977
  • 1
  • 21
  • 32
3

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);
    }
}
mxmissile
  • 11,464
  • 3
  • 53
  • 79
Sunil Purushothaman
  • 8,435
  • 1
  • 22
  • 20
  • 4
    You'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
2

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.

granadaCoder
  • 26,328
  • 10
  • 113
  • 146
1
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)
    {

    }
}
psdehr
  • 21
  • 1
  • 5
  • 1
    Why the parameter `files` is `ref`? There is no need. – Massimiliano Kraus Dec 15 '16 at 08:41
  • @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
1
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);
Nitin Sawant
  • 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
1

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);
        }
    }
}
Akbar Badhusha
  • 2,415
  • 18
  • 30
1

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;
        }
    }
}
Fidel
  • 7,027
  • 11
  • 57
  • 81
1

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;
    }
  }
}
jeromej
  • 10,508
  • 2
  • 43
  • 62
1

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
brainoverflow98
  • 1,138
  • 11
  • 28
0

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;
}
B. Clay Shannon-B. Crow Raven
  • 8,547
  • 144
  • 472
  • 862
0

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);
D.Kempkes
  • 345
  • 3
  • 5
0

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.)

Sven Viking
  • 2,660
  • 21
  • 34
-1

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;
    }
Brad Larson
  • 170,088
  • 45
  • 397
  • 571
Sardoan
  • 767
  • 1
  • 10
  • 25
-1
var d = new DirectoryInfo(@"C:\logs");
var list = d.GetFiles("*.txt").Select(m => m.Name).ToList();
Sameera R.
  • 4,384
  • 2
  • 36
  • 53
-2

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;
    }
-3
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();
        }