Link to home
Start Free TrialLog in
Avatar of sg08234
sg08234Flag for Germany

asked on

Find newest file (batch)

I need a (little?) batch which set an encironment variable to the freshest file in a certain directory (and all its subdirectories).

Unfortunately    DIR /OD /S /TC /B    only sorts wihtin each subdirectory (no "global" sort).

Many thanks in advance
Michael
Avatar of Shift-3
Shift-3
Flag of United States of America image

I don't know of a way to do this in batch without using a silly workaround (i.e. copying each subdirectory's newest file to another folder and then sorting that).  Is vbscript acceptable?
Avatar of Qlemo
With a one-liner, which sets nf as the resulting filename, and needs robocopy.exe (of course you can expand it to several lines, usage in a batch file aso.):

(for /F "tokens=*" %L in ('robocopy . . /IS /L /NJH /NJS /TS /S /NS /NC') do @(set "lin=%L"& if "!lin:~0,1!" == "2" (echo !lin:~0,20!!dir!!lin:~20!)
else (set "dir=!lin!"))) | sort /r > tmp.$$$ & set /P nf=<tmp.$$$ & set nf=!nf:~20! & del tmp.$$$

Open in new window

This batch file will set the environment variable LASTFILE to the newest file in the current directory including all sub-sirectories.


@rem NEWESTF.BAT - Written by Paul Tomasi 2009
@echo off
setlocal enabledelayedexpansion

set l=
set d=99999999
set t=9999

for /f "tokens=*" %%a in ('dir /b /a-d /s') do (
   set dttmp=%%~ta
   set dtmp=!dttmp:~6,4!!dttmp:~3,2!!dttmp:~0,2!
   set ttmp=!dttmp:~-5,2!!dttmp:~-2,2!
   if !dtmp! LEQ !d! (
      if !dtmp! LSS !d! (
         set d=!dtmp!
         set t=!ttmp!
         set l=%%a
      ) else (
         if !ttmp! LSS !t! (
            set d=!dtmp!
            set t=!ttmp!
            set l=%%a
         )
      )
   )
)

endlocal & set lastfile=%l%
POSTED AGAIN DUE TO SLIGHT IMPROVEMENT

This batch file will set the environment variable LASTFILE to the newest file in the current directory including all sub-sirectories.


@rem NEWESTF.BAT - Written by Paul Tomasi 2009
@echo off
setlocal enabledelayedexpansion

set d=99999999
set t=9999

for /f "tokens=*" %%a in ('dir /b /a-d /s') do (
   set dt=%%~ta
   set dtmp=!dt:~6,4!!dt:~3,2!!dt:~0,2!
   set ttmp=!dt:~-5,2!!dt:~-2,2!
   if !dtmp! LEQ !d! (
      if !dtmp! LSS !d! (
         set d=!dtmp!
         set t=!ttmp!
         set l=%%a
      ) else (
         if !ttmp! LSS !t! (
            set d=!dtmp!
            set t=!ttmp!
            set l=%%a
         )
      )
   )
)

endlocal & set lastfile=%l%
Such a approach was in my mind, too, but I didn't like it, because it would not work for me (differing date format), and each file is queried twice (once by dir and once by %%~ta).
Qlemo... you are truly a budding programmer....

I used a programmer's approach to solving this problem. Notice how i compare dates first to lessen the load during comparison. only if the filedates are greater then no more comparison takes place otherwise dates are compared to whittle it down to a choice.

the problem i had (and this is something i confess to not doing in the past) is to make the variable persistent. I worked it out in the end - but that alone took nearly an hour of my time.

i would have found it interesting to have seen your code along the same approach.

t0t0, there's something wrong.

I am set to UK Time like you, but your batch file is finding the following file and reporting it as being the "freshest":
C:\UNZIP\Excel_For_John\macrofun\Macrofun.cnt

I ran the batch file from:  C:\UNZIP, which contains 1,585 files in 282 sub-folders.

I am immediately aware that  have not accessed that file, or its associated *.HLP file, for ages.  On checking its properties I get this:

Created: 15 August 1995, 09:16:24
Modified: 15 August 1995, 09:16:24
Accessed: 28 February 2009, 08:20:36

The Current time at writing this was 08:20 on 28/02/2009, so that would appear to be rendering the correct results to somebody who wasn't aware of what files actually had been created, modified, or accessed in a certain folder.  In this case I am immediately aware that the last access date being found (having been applied to the file's properties) was NOT by me, or by any other system function that I am aware of.  It was presumably accessed by your batch file querying it.

It seems strange that this file should be the last one to be accessed by the batch file, as the date and time tends to suggest, because its containing folder sits somewhere just above half way alphabetically in all the sub-folders of C:\UNZIP, and the file name isn't like it's alphabetically last or first in the queue.  The file size is only 182 bytes, and I do know that there are a number of files in all of the sub-folders in C:\UNZIP that are smaller.  The same is true of the actual folder size (or more correctly the combined size of the files within it).  There are folders with smaller, and of course greater, content than this.

So, I'm puzzled by your results.  Clearly your batch file isn't giving the same results as with your own computer.

As a test, I created a couple of new files, then edited and resaved a couple more immediately before re-running your batch file.  It reported the same file again.

Back to the drawing board perhaps?
Apologies sg08234. IMPORTANT NOTE: The following code correctly returns the oldest file.

BillDL, blimey.... you've just twigged my mind.... it's the LATEST file i should be returning NOT the OLDEST..... a simple modification to a simple oversight will reverse this. See below.

BillDL, thank you for spotting this. As the program stood though, it returned the OLDEST file in it's search path so, two programs for the price of one then.... Can't be bad eh?


@rem NEWESTF.BAT - Written by Paul Tomasi 2009
@echo off
setlocal enabledelayedexpansion

set d=00000000
set t=0000

for /f "tokens=*" %%a in ('dir /b /a-d /s') do (
   set dt=%%~ta
   set dtmp=!dt:~6,4!!dt:~3,2!!dt:~0,2!
   set ttmp=!dt:~-5,2!!dt:~-2,2!
   if !dtmp! GEQ !d! (
      if !dtmp! GTR !d! (
         set d=!dtmp!
         set t=!ttmp!
         set l=%%a
      ) else (
         if !ttmp! GTR !t! (
            set d=!dtmp!
            set t=!ttmp!
            set l=%%a
         )
      )
   )
)

endlocal & set lastfile=%l%
This SEEMS to work, reporting the last file I modified and resaved.
I just made the test all zeros and tested for Greater Than rather than Less Than:
@rem NEWESTF.BAT - Written by Paul Tomasi 2009
@rem Whatever.bat - Modified by Bill Dalziel so it now seems to work
@echo off
setlocal enabledelayedexpansion
 
set d=00000000
set t=0000
 
for /f "tokens=*" %%a in ('dir /b /a-d /s') do (
   set dt=%%~ta
   set dtmp=!dt:~6,4!!dt:~3,2!!dt:~0,2!
   set ttmp=!dt:~-5,2!!dt:~-2,2!
   if !dtmp! GEQ !d! (
      if !dtmp! GTR !d! (
         set d=!dtmp!
         set t=!ttmp!
         set l=%%a
      ) else (
         if !ttmp! GTR !t! (
            set d=!dtmp!
            set t=!ttmp!
            set l=%%a
         )
      )
   )
)
 
endlocal & set lastfile=%l%
 
echo %lastfile%
 
echo.
pause
exit

Open in new window

Aha, you were eating your dinner on Friday night when writing it, or else your dinner was getting cold and you were hurrying.

While looking at why your file wasn't working, I just exported to a *.csv file using a few lines in a batch file, then added autofilters.  That revealed just as you stated, that the file was indeed the oldest.

Good, so it's working fine now, and as you say you now have 2 batch files :-)
Line 28 is a good one, t0t0! Nice trick to get variable l persistent!
FYI, my according solution (including that trick, and with same date format) is:


@echo off
setlocal enabledelayedexpansion
 
set dt=00000000-00:00
for /R %%a in (*) do (
  set tmp=%%~ta
  set tmp=!tmp:~6,4!!tmp:~3,2!!tmp:~0,2!-!tmp:~-5!
  if !tmp! GTR !dt! (
    set dt=!tmp!
    set file=%%a
  )
)
endlocal & set file=%file%& set dt=%dt%
echo %dt%   %file%

Open in new window

Do you know something Qlemo.... I like your code.... I should say I like my code, but, I'm drawn to yours because of it's simplicity. If I had to  re-write mine again it wouldn't look almost identical to yours.

I just noticed the "FOR /R".... I like it.... no messing about witn "/A-D /B /S" or tokens etc.... in fact I just banged a one-liner into CMD to verify and remind myself it only returns filenames.... It's funny how we get so accustomed to doing almost everything in FOR /F we sometimes forget the the easier FOR /R....

Back to your code though.....I'd've probably been tempted to use just a '.' instead of the '*' - purely because it preserves white space and does not look as imposing....

Oh, by the way, I notice your entire code is written lowercase except for the '/R'... was this intentional on your part to draw our attention to the 'correct' FOR construction?.. Anyway, I did notice, and it looks good.

Thank you for your kind words regarding passing back a variable from localisation. It took me a lot of trial and error and a long time to figure that one out - I even shouted at my kiddy at one point!

OH MY GOD!!! I just tried the '.' instead of the '*' and I won't tell you what I got - I don't want to spoil the surprise for when you try it out yourselves!!! Wow! You learn something everyday.... how very true that really is!

As before, it's been a pleasure.

PS. BillDL, how come you didn't throw something together yourself?
t0t0,

I always write the for option in uppercase, to draw MY attention to it ;-). I try to stick on having the for variable in uppercase, too, to make the %~nX stuff easier to read. As you might have noticed, I forgot to use mixed case in the setlocal - usually, it's

setlocal EnableDelayedExpansion

to make that nice and pretty, and helps me not to make a typo there.

About the (.) in for /R - surprise, suprise, and it's even documented!
t0t0
>>> "PS. BillDL, how come you didn't throw something together yourself?" <<<

I did, but was still working on it when I saw that you had posted.  I suspended what I was doing and made it my mission to find out why it hadn't worked on my PC, because it looked sound.  I then spotted the comparison issue with 9's instead of 0's and noted, on posting my findings, that you had a working solution.  sq08234 has stated "I need a (little?) batch ...", and that's what you provided.  Job done, and well done, as far as I was concerned.  Solution provided as asked, and no sense in cluttering it up with my clunky huge batch file.

I'm always curious to see how many ways there are to circumvent an apparent restriction getting in the way and causing someone else's first tests to fail.  In life I like challenging authority, and if someone says something doesn't work, or that I can't do it, I try to disprove it or do it anyway and avoid being caught (depending on the situation and punitive consequences).  I usually can find a way (although I usually get caught), and it's rewarding when I have beaten a rule.  But with batch files it usually ends up very long winded because of my debugging step-wise approach rather than nested approach that I find harder to dubug when experimenting.  I'm always watching and learning.

In this case I embarked on disproving the DIR command limitation highlighted by sg08234.  After filtering 6 or more (can't recall because I've now deleted them) temp *.txt files with the DIR, FOR, FIND and SORT commands used at different stages, I got to the point of verifying a couple of apparent successes using Excel.  Starting off with the DIR command it IS possible to get the freshest file from ALL sub-folders, but you first have to generate a folder list, process each folder separately, create a master list of the freshest files in each folder, and finally filter that and only output the last found.  Very long winded, but it seemed to have beaten the system.  A bit like travelling along 20 back roads at 75 mph to avoid speed cameras and getting to the destination an hour later, rather than just travelling on 5 main roads and adhering to the speed limit to get there on time.

Meantime I had been at a standstill with a "normal" method involving the COMBINED Date-Time stamp.  Something along the lines of what Qlemo posted where the Date AND Time stamps from each file are fetched together with %%~ta, and then modified to remove the / delimeters to end up with "yymmdd-hh:mm".  I was trying to use "yyyymmdd-hhmm" (no colon and slightly different modifiers).  However, I couldn't figure the best way to compare the file stamps, so I was glad when I (and you) realised the simple error in your batch file and Qlemo posted a variation.  I'm bald enough and can't affort to pull out much more hair ;-)

Bill
Hiya...

Thought I'd re-write my original code. This is smpler and slicker. I didn't do it this way previously because I didn't realise we could compare 12-digit integers (must test the limits on this one day).

I've also simplified the comparison to just 'GTR' making it run faster than 'GEQ'.


@echo off
setlocal enabledelayedexpansion
set latest=000000000000
for /r %%a in (*) do (
   set dt=%%~ta
   set datetime=!dt:~6,4!!dt:~3,2!!dt:~0,2!!dt:~-5,2!!dt:~-2,2!
   if !datetime! GTR !latest! set latest=!datetime! & set file=%%a
)
endlocal & set lastfile=%file%
sg08234

Please view the previous comment (ID: 23767574) and decide whether it meets your requirements.

Thank you.
ASKER CERTIFIED SOLUTION
Avatar of t0t0
t0t0
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of sg08234

ASKER

Ok for me - how do I split?

Thanks - Michael
Press "Request Attention", write "Please re-open for different point assignment" or the like as reason, and after opened again, you can split points.
I have no problem splitting points with Qlemo however, I also think BillDL desrves a split too for being a good sport in identifying an early flaw in the logic of my first program even though he did not submit any code himself.

I was pleased Qlemo, who I have always held in high regard, adapted the output of his code based on my own code as the technique used took much trial and error in arriving at and as far as I am aware is undocumented, or at least, I haven't come across in my readings. It's nice to know, as experts we are able to learn from each other in the process of providing a solution, and on this occasion I felt it was a compliment to me.

I have absolutely no objection to any expert taking from my code to improve his own code as it serves to establish standards and provides for better solutions.
Don't bother including me.  Although I did spot the simple error, it was one that would have been quickly realised anyway.  t0t0 and Qlemo created some fine results separately and in collusion, and they effectively answered the question together.
BillDL.. Please substitue the word "collusion" with "collaboration".... only, culliusion implise we conspired. See below.

noun
1. a secret agreement, esp. for fraudulent or treacherous purposes; conspiracy: Some of his employees were acting in collusion to rob him.  

2. Law. a secret understanding between two or more persons to gain something illegally, to defraud another of his or her rights, or to appear as adversaries though in agreement: collusion of husband and wife to obtain a divorce.

Thank you Bill
Hmmm.  Yes, that's what it could appear to infer or imply, although it was certainly not intended to be perceived as such, given that it was also qualified by the other key words expressing something rather less accusatory:

"fine results separately and in collusion, and they effectively answered the question together".

It's easy to get really pedantic with definitions.  The verb "conspire" is sometimes defined as "to act in harmony toward a common end", with no implications of nefarious intentions.  In actual fact, the "Law" definition above just about sums up Experts-Exchange in general:  "to appear as adversaries though in agreement".  One of the definitions of the intransitive verb "collaborate" is: "to cooperate with or willingly assist an enemy of one's country and especially an occupying force", which may sound sinister until you read it again and realise that it also sums up Experts Exchange in many ways.

OK, "collusion" was a poor choice of word, and something we are all guilty of doing on the odd occasion.  My apologies to you both.
Thank you for the clarification Bill.
Avatar of sg08234

ASKER

Many thanks to both of you. I already accepted an early version as solution (even if it gave me the oldest file) - but now the solutions became slimline.
sg08234
 
Avatar of ITLsupport
ITLsupport

The old code is still good, has came as a great help

Thanks
Blimey! This is a blast from the past!

paultomasi (formerly t0t0)
Using your batch file is there a way to copy the latest file with a particular file extension?

My scenario is that I have a continuous wav file which auto renews after an hour. It then compresses that file to mp3.

Since I can only run the batch file after compression on the wav, using the latest file means it would try to copy the wav as this is the newest file and not the mp3 which I need.

I have attached an example 8-40 is the current newest file wav whilst 8-20 is the mp3 one required.
ScreenHunter-01-Apr.-17-08.42.gif
ITLsupport

>> "...is there a way to copy the latest file with a particular file extension?"

Try this:
@echo off
setlocal enabledelayedexpansion
set latest=000000000000

for %%a in (*.mp3) do (
   set dt=%%~ta
   set datetime=!dt:~6,4!!dt:~3,2!!dt:~0,2!!dt:~-5,2!!dt:~-2,2!
   if !datetime! GTR !latest! set latest=!datetime! & set latestfile=%%a
)

echo %latestfile% is the latest mp3 file

Open in new window

Please open a separate question with a link next time. Thank you.