How do I find the most recently created file in a directory from a batch file?


We've reached Hump Day of Batch File Week. Remember, nobody actually likes batch programming. You merely tolerate it.

Today, we'll find the most recently-created item in a directory. (For example, we have a server that holds our daily builds, and you might want to write a batch file that automatically installs the latest build.)

There may be better ways, but what I do is ask for a list sorted oldest-to-newest, and then choose the last one.

for /f %%i in ('dir /b/a-d/od/t:c') do set LAST=%%i
echo The most recently created file is %LAST%

This trick works by asking the dir command to list just the names (/b) of just the files /a-d, sorted by date (/od), based on the creation time (/t:c).

Each time a new file is reported, its name is stored in the LAST variable, overwriting the previous one. When the loop finishes, the LAST variable contains the name of the newest file, since that's the one that didn't get overwritten.

You can tweak the command line to perform other queries. For example, if you want the newest file, then just ask for a reverse sort (/o-d). If you want the file sorted by modified time rather than creation time, then use /t:w. You get the idea.

Limitations: The implementation above assumes that no files contain spaces in their name. Removing this limitation is left as an exercise.

Comments (29)
  1. pndmnm says:

    My favorite way to do this:

    FOR /f %%i IN ('dir /o:-d /b') DO (set LAST=%%i

    goto stop)

    :stop

  2. Similar to @pndmnm, I do:

    for /f "tokens=*" %%a in ('dir /A:D /B /O:-D %BUILDDROP%') do set NEWEST=%%a&& goto :next

    :next

    In case there are a large number of objects in the BUILDDROP directory, it doesn't have to enumerate them all

    [Well, except that the "dir" command enumerated them all (in order to sort). -Raymond]
  3. if you want the newest file, then just ask for a reverse sort (/o-d)

    s/newest/oldest/

  4. In PowerShell:

    $files = Get-ChildItem | Where-Object { !$_.PsIsContainer } | Sort-Object { $_.CreationTime }

    Write-Host "The most recently created file is $($files[-1].Name)"

    I didn't use aliases for clarity (short version of the first command would look like "gci |? { !$_.PsIsContainer } | sort { $_.CreationTime }").

    To get the oldest file, just use $files[0] (or use Sort-Object -Descending).

    You can even do it in one line:

    "The most recently created file is $((gci |? { !$_.PsIsContainer } | sort { $_.CreationTime })[-1].Name)"

    Though I wouldn't recommend it because of readability. Note that the Write-Host can be omitted since the default action for an expression is to write it to the output anyway.

    [I think you're missing the point of Batch File Week. -Raymond]
  5. Jeff says:

    @rbirkby: "tokens=*" will still lose leading spaces (which are legal in Windows filenames). Use "delims=" to preserve all spaces in each line.

  6. 640k says:

    What happens when the folder has 0 files?

  7. 640k says:

    The last modified time is untrustworthy on Vista and newer OS.

  8. Muzer says:

    Loving batch file week; I'd love to see more.

  9. Cesar says:

    @640k: Do you have any references on that? The mtime being unreliable would confuse basic tools like make, so I am surprised about that claim.

  10. alegr1 says:

    The last modified time is untrustworthy on Vista and newer OS.

    You mean "last accessed" time?

  11. Douglas says:

    @Raymond

    While it's true that posting a Power Shell equivalent script is "missing the point", I think it's useful and interesting to see them here.

  12. Raymond: "I think you're missing the point of Batch File Week."

    No I'm not, I'm just giving an alternative in what I think is an underappreciated scripting environment. I know that you probably know about it, but not all of your readers might. Surely there is no harm in it?

    [If somebody has an article during COBOL week on how to do a calculation in COBOL, is it appropriate to say, "Here's a much easier way to do it in APL"? Even if you think APL is an underappreciated programming language? -Raymond]
  13. DCL says:

    Screw batch files. I'm gonna learn Power Shell.

  14. Jim says:

    @rbirkby:

    Worse than Raymond's point, in my view, is that it's a premature optimisation. Unless you know from experience that this is definitely causing a slowdown, any optimisation is much more likely to introduce a bug than do anything useful.

  15. Troy Martin says:

    I think next time someone says that batch files are hideously simple and featureless, I'm going to show them this post…

  16. Brian_EE says:

    @DCL:

    I remember "programming" in DCL on a VAX 6410 during college. We actually wrote several interactive (and non-trivial) games completely in the command interpreter. Ahh, the good old days…..

  17. Adam Rosenfield says:

    Please bring back CLR week, I actually learned something useful from that.  I'll stick with Cygwin bash for task automation, thank you very much.  For the record, here's how you'd do that in bash:

    LAST="$(stat -c '%W %n' * | sort -k1n | tail -1 | cut -d' ' -f2-)"

    It's a little ugly since ls(1) doesn't know about the file's creation time, so you have to stat(1) everything, sort the results, and parse out the filename.  But if you care about the file with the newest modification time instead of creation time, it's much simpler:

    LAST="$(ls -t | head -1)"

    It even works with filenames with spaces in them [but not newlines (which are invalid on NTFS, but not on all file systems); that's left as an exercise for the reader].

  18. Kevin says:

    @Adam, shouldn't your second example have a -1 on the ls to ensure it uses a single column?

  19. voo says:

    @Sven Groot: I agree, PowerShell is at the moment even my favorite default shell, which considering we had to live with command.com/exe for decades under windows is nothing short of astounding. Anyone using batch files in environments that only have to work in vista+ is insane imo – alas XP support is still often necessary (but at least I can write my own throw away scripts always in PowerShell!)

  20. John says:

    If you have a newish version of Windows (I believe Vista/Server 2008 or better) I've found the forfiles (technet.microsoft.com/…/cc753551%28v=ws.10%29.aspx) command to be invaluable for my little batch files that clean out old log files:

    forfiles -p "C:whateverfolder" -s -m *.* -d <+/-number of days> -c "cmd /c del @path"

  21. Brian G says:

    Sorry for continuing the off-topic meandering, Raymond, but…

    @Kevin, the mere act of piping the output of ls (rather than allowing it to dump to a terminal display) has always forced it into a single column in my experience.  Perhaps this isn't true on all systems, but it's my experience with GNU bash and ls on Linux, Cygwin, msysgit on Windows, etc.  Look at the different output produced by "ls" and "ls | cat", for example.

  22. Raphael says:

    @Adam

    Actually, newlines *are* valid on NTFS. Creating a file with one in the name is a different matter.

  23. Neil says:

    @Adam that's because historically Unix never stored the creation time of a file.

    @John Thanks for mentioning forfiles. Interestingly it aborts on an access denied error (caused by a handle not closed when interrupting a for /r loop, oops!)

  24. Mikalai says:

    As an exercise – just add "delims=?" – specify any filenames restricted symbol. So it would be something like for /f "delims=?" %%i in ('dir /b/a-d/od/t:c') do set LAST=%%i

  25. Grzechooo says:

    I didn't know you could write dir's parameters without space.

  26. Skyborne says:

    @Grzechooo, it turns out you don't even need the space between the command and first parameter (as in dir/w)–because '/' isn't a valid path character, it can't be part of the executable, so it gets handled as an arg.  When you have structure in your system, you can take advantage of it.

  27. @Raymond: I'm not familiar with APL, but assuming that APL is used for similar scenarios as COBOL, is available in most current environments where COBOL is used, and is generally more recommended for new development then yes, I would consider it appropriate.

    PowerShell is intended for the same kind of tasks as batch files, and can do all the things that batch files do, much more easily. It's available on all versions of Windows since Vista, which is where more people are probably using batch files. And there are probably quite a few people who are still writing new batch files today because they're not aware of PowerShell. As such, I consider it relevant enough that there may be some people who read this blog who would be interested in it.

    You're doing a series of articles about an old, archaic, but still often used form of shell scripting in Windows, and you weren't expecting people to point out there's a newer, easier and more powerful alternative baked into the OS nowadays?

    [This is Batch File Week, not PowerShell week. I already noted at the start of the week that "Yes, this is all easier to do in PowerShell. That's not the point of Batch File Week." By your own logic, you be spending your time in assembly language programming forums saying "This is much easier to do in C". I'm assuming that if you're writing batch files, it's because you have to, not because you choose to. (For example, you may not have sufficient privileges to enable PowerShell scripting.) -Raymond]
  28. Rajbeer Dhatt says:

    @Sven:  Perhaps it would make more sense to you if Raymond called it "masochism week" ?  Really, how many readers of this blog don't know PowerShell exists?

    Keep it up, I'm loving Batch File week.

  29. Veltas says:

    "Remember, nobody actually likes batch programming. You merely tolerate it."

    I dunno, it's pretty refreshing to have something that gets to file and directory modifications so easily.  In fact I'd suggest people don't use them enough; they make such 'batch' tasks so easy and yet I rarely see them…

Comments are closed.

Skip to main content