174

I need to find the most recently modified file in a directory.

I know I can loop through every file in a folder and compare File.GetLastWriteTime, but is there a better way to do this without looping?.

Shin
  • 664
  • 2
  • 13
  • 30
Chris Klepeis
  • 9,783
  • 16
  • 83
  • 149
  • 20
    No there is no better way which avoids looping. Even using LINQ just hides the looping into some deeper functionality where you can't see it directly. – Oliver May 17 '10 at 11:52
  • 2
    If you wanted to find the most recently modified file(s) on the whole filesystem, then the NTFS Change Journal would be useful. Very very hard to use from C#, though. – Ben Voigt May 27 '14 at 20:56

11 Answers11

375

how about something like this...

var directory = new DirectoryInfo("C:\\MyDirectory");
var myFile = (from f in directory.GetFiles()
             orderby f.LastWriteTime descending
             select f).First();

// or...
var myFile = directory.GetFiles()
             .OrderByDescending(f => f.LastWriteTime)
             .First();
Scott Ivey
  • 40,768
  • 21
  • 80
  • 118
  • 106
    Personally, I find that the non-sugared version is easier to read: `directory.GetFiles().OrderByDescending(f => f.LastWriteTime).First()` – Jørn Schou-Rode Jul 24 '09 at 20:32
  • 6
    yeah, i agree most of the time too - but when giving examples the query syntax makes it a bit more obvious that it's a linq query. I'll update the example with both options to clarify. – Scott Ivey Jul 24 '09 at 20:48
  • 3
    Thanks! Now I just need to convince my boss to expedite the process of upgrading us from .net 2.0 so I can use Linq :) – Chris Klepeis Jul 24 '09 at 20:54
  • 3
    you can use linq with 2.0 SP1 with a little extra work - just reference the System.Core.dll file from 3.5, and set it to "copy local" – Scott Ivey Jul 24 '09 at 21:19
  • 2
    Nice, must be a loop under there somewhere. :) I guess if you don't see it it's not there. +1 – kenny Aug 31 '11 at 12:12
  • 1
    There is still a loop right? I guess he is looking for a system call where you can specify a filter based on the date and the collection returned contains only the latest file. But again I do not think that looping make much difference here in terms of performance if the details about files are returned from NTFS. – Faiz Jul 22 '13 at 06:21
  • 1
    What if the directory is in a server... (e.g. \\myserver\myfolder\files.csv)? – Si8 Apr 09 '14 at 19:06
  • 3
    @SiKni8, it should work the same. You'd just need to escape your backslashes. DirectoryInfo(@"\\myserver\myfolder") or DirectoryInfo("\\\\myserver\\myfolder") should both work. – Scott Ivey Apr 10 '14 at 13:07
  • 1
    Only the later one works :) Thank you for the response. – Si8 Apr 10 '14 at 13:19
  • 3
    Careful using LastWriteTime, this value will return January 1st, 1600 if the file has never been modified. This will mess your sorting up. – ProVega May 23 '14 at 22:03
  • 12
    Shouldn't this use `FirstOrDefault()` instead of `First()`? The latter will cause an `InvalidOperationException` if the directory is empty. – Tobias J May 26 '15 at 22:26
  • Is there any way to ignore temporary files? If I have one of the files open in the directory I am searching, I get the temporary file returned. – SkinnyPete63 Oct 30 '18 at 12:27
  • SkinnyPete63 You can use a searchPattern on the GetFiles method eg."\*.pdf" Or "Ab?D*.??F" – GregJF Mar 25 '19 at 04:56
37

Expanding on the first one above, if you want to search for a certain pattern you may use the following code:

using System.IO;
using System.Linq;
...
string pattern = "*.txt";
var dirInfo = new DirectoryInfo(directory);
var file = (from f in dirInfo.GetFiles(pattern) orderby f.LastWriteTime descending select f).First();
Gabriel Amorim
  • 370
  • 2
  • 13
Zamir
  • 401
  • 4
  • 2
21

If you want to search recursively, you can use this beautiful piece of code:

public static FileInfo GetNewestFile(DirectoryInfo directory) {
   return directory.GetFiles()
       .Union(directory.GetDirectories().Select(d => GetNewestFile(d)))
       .OrderByDescending(f => (f == null ? DateTime.MinValue : f.LastWriteTime))
       .FirstOrDefault();                        
}

Just call it the following way:

FileInfo newestFile = GetNewestFile(new DirectoryInfo(@"C:\directory\"));

and that's it. Returns a FileInfo instance or null if the directory is empty.

Edgar Villegas Alvarado
  • 18,204
  • 2
  • 42
  • 61
  • 15
    Or you can use the [recursive search](http://msdn.microsoft.com/en-us/library/ms143327(v=vs.110).aspx) option. – ricksmt May 12 '14 at 22:12
10

A non-LINQ version:

/// <summary>
/// Returns latest writen file from the specified directory.
/// If the directory does not exist or doesn't contain any file, DateTime.MinValue is returned.
/// </summary>
/// <param name="directoryInfo">Path of the directory that needs to be scanned</param>
/// <returns></returns>
private static DateTime GetLatestWriteTimeFromFileInDirectory(DirectoryInfo directoryInfo)
{
    if (directoryInfo == null || !directoryInfo.Exists)
        return DateTime.MinValue;

    FileInfo[] files = directoryInfo.GetFiles();
    DateTime lastWrite = DateTime.MinValue;

    foreach (FileInfo file in files)
    {
        if (file.LastWriteTime > lastWrite)
        {
            lastWrite = file.LastWriteTime;
        }
    }

    return lastWrite;
}

/// <summary>
/// Returns file's latest writen timestamp from the specified directory.
/// If the directory does not exist or doesn't contain any file, null is returned.
/// </summary>
/// <param name="directoryInfo">Path of the directory that needs to be scanned</param>
/// <returns></returns>
private static FileInfo GetLatestWritenFileFileInDirectory(DirectoryInfo directoryInfo)
{
    if (directoryInfo == null || !directoryInfo.Exists)
        return null;

    FileInfo[] files = directoryInfo.GetFiles();
    DateTime lastWrite = DateTime.MinValue;
    FileInfo lastWritenFile = null;

    foreach (FileInfo file in files)
    {
        if (file.LastWriteTime > lastWrite)
        {
            lastWrite = file.LastWriteTime;
            lastWritenFile = file;
        }
    }
    return lastWritenFile;
}
bluish
  • 26,356
  • 27
  • 122
  • 180
TimothyP
  • 21,178
  • 26
  • 94
  • 142
  • 1
    Sorry , didn't see the fact that you did not want to loop. Anyway... perhaps it will help someone searching something similar – TimothyP Nov 23 '09 at 07:52
  • 3
    This code does not compile. - lastUpdatedFile should not be an array. - The initial value for lastUpdate is invalid (0001/0/0). – Lars A. Brekken May 13 '10 at 21:38
8

Short and simple:

new DirectoryInfo(path).GetFiles().OrderByDescending(o => o.LastWriteTime).FirstOrDefault();
Jacob
  • 3,598
  • 4
  • 35
  • 56
4

Another approach if you are using Directory.EnumerateFiles and want to read files in latest modified by first.

foreach (string file in Directory.EnumerateFiles(fileDirectory, fileType).OrderByDescending(f => new FileInfo(f).LastWriteTime))

}
Gaurravs
  • 639
  • 1
  • 15
  • 30
3

You can react to new file activity with FileSystemWatcher.

Scott Marlowe
  • 7,915
  • 10
  • 45
  • 51
3

it's a bit late but...

your code will not work, because of list<FileInfo> lastUpdateFile = null; and later lastUpdatedFile.Add(file); so NullReference exception will be thrown. Working version should be:

private List<FileInfo> GetLastUpdatedFileInDirectory(DirectoryInfo directoryInfo)
{
    FileInfo[] files = directoryInfo.GetFiles();
    List<FileInfo> lastUpdatedFile = new List<FileInfo>();
    DateTime lastUpdate = DateTime.MinValue;
    foreach (FileInfo file in files)
    {
        if (file.LastAccessTime > lastUpdate)
        {
            lastUpdatedFile.Add(file);
            lastUpdate = file.LastAccessTime;
        }
    }

    return lastUpdatedFile;
}

Thanks

2

Here's a version that gets the most recent file from each subdirectory

List<string> reports = new List<string>();    
DirectoryInfo directory = new DirectoryInfo(ReportsRoot);
directory.GetFiles("*.xlsx", SearchOption.AllDirectories).GroupBy(fl => fl.DirectoryName)
.ForEach(g => reports.Add(g.OrderByDescending(fi => fi.LastWriteTime).First().FullName));
Michael Bahig
  • 748
  • 8
  • 17
0

I do this is a bunch of my apps and I use a statement like this:

  var inputDirectory = new DirectoryInfo("\\Directory_Path_here");
  var myFile = inputDirectory.GetFiles().OrderByDescending(f => f.LastWriteTime).First();

From here you will have the filename for the most recently saved/added/updated file in the Directory of the "inputDirectory" variable. Now you can access it and do what you want with it.

Hope that helps.

JasonR
  • 399
  • 4
  • 8
  • 26
  • To add on to this, if your goal is to get back the file path, and you're using FirstOrDefault because it's possible that there are no results, you can use the null-conditional operator on the FullName property. This will return you back "null" for your path without having to save off the FileInfo from FirstOrDefault before you call FullName. string path = new DirectoryInfo(path).GetFiles().OrderByDescending(o => o.LastWriteTime).FirstOrDefault()?.FullName; – StalePhish Jun 15 '18 at 19:57
0
private List<FileInfo> GetLastUpdatedFileInDirectory(DirectoryInfo directoryInfo)
{
    FileInfo[] files = directoryInfo.GetFiles();
    List<FileInfo> lastUpdatedFile = null;
    DateTime lastUpdate = new DateTime(1, 0, 0);
    foreach (FileInfo file in files)
    {
        if (file.LastAccessTime > lastUpdate)
        {
            lastUpdatedFile.Add(file);
            lastUpdate = file.LastAccessTime;
        }
    }

    return lastUpdatedFile;
}
bluish
  • 26,356
  • 27
  • 122
  • 180