Page 1 of 3

Finding newest file in all subdirectories

Posted: 23 Apr 2012 11:23
by smccaffr39
hello all. i need a DOS batch script that can find the newest file in all sub-directories. i have tried this:

SET Path=\\server\path
FOR /F "delims=|" %%I IN ('DIR "%Path%" /B /S /A:-D /O:D') DO (SET NewestFile=%%I)

but the problem is that it ends up returning the newest file from the last folder it parsed. i need the newest of all sub-directories parsed. ideas?

thanks in advance,

Steve

Re: Finding newest file in all subdirectories

Posted: 23 Apr 2012 12:56
by Squashman
You will need nested for loops. The first one to get a list of the directories, then a 2nd one to then list the files within that directory. I wouldn't use PATH as a variable name in a batch file unless you actually want to change that environmental variable.

Code: Select all

SET sPath=\\server\path
pushd "%sPath%"
FOR /F "delims=" %%I in ('dir /ad /s') do (
     pushd "%%~I"
     FOR /F "delims=" %%G IN ('DIR /B /A:-D /O:D') DO SET NewestFile=%%G
     popd
)
popd

Re: Finding newest file in all subdirectories

Posted: 23 Apr 2012 13:40
by dbenham
I am not clear about the question.

Do you want the single most recently modified file across all subdirectories? (total of one file)

Or do you want the most recently modified file for each subdirectory? (one file per directory)


Dave Benham

Re: Finding newest file in all subdirectories

Posted: 24 Apr 2012 06:42
by smccaffr39
dbenham wrote:I am not clear about the question.

Do you want the single most recently modified file across all subdirectories? (total of one file)

Or do you want the most recently modified file for each subdirectory? (one file per directory)


Dave Benham


hi Dave. i need a total of one file retrieved from all sub-directories.

Steve

Re: Finding newest file in all subdirectories

Posted: 24 Apr 2012 07:47
by smccaffr39
Squashman wrote:You will need nested for loops. The first one to get a list of the directories, then a 2nd one to then list the files within that directory. I wouldn't use PATH as a variable name in a batch file unless you actually want to change that environmental variable.

Code: Select all

SET sPath=\\server\path
pushd "%sPath%"
FOR /F "delims=" %%I in ('dir /ad /s') do (
     pushd "%%~I"
     FOR /F "delims=" %%G IN ('DIR /B /A:-D /O:D') DO SET NewestFile=%%G
     popd
)
popd


thanks Squashman, that's an elegant solution. looks like i'm on my way. i was getting a lot of invalid file locations so include /b in the first for's dir. now i'm only getting a bunch of "File Not Found", which is fine. i'd like to tweak this to also set a variable that stores the path that the newest file is in. i was also thinking of storing its date so comparisons can be made in one for loop, but that would probably fail across computers because of it's being set by regional settings. have any good date comparison suggestions?

Re: Finding newest file in all subdirectories

Posted: 24 Apr 2012 07:50
by Squashman
Date comparisons in batch are not my forte.

Read the help for the FOR command. You will see how to get the Path and Date of the current file the for loop is working on. It is at the very end of the help.

Re: Finding newest file in all subdirectories

Posted: 24 Apr 2012 09:14
by smccaffr39
good stuff. thanks for your help!

Re: Finding newest file in all subdirectories

Posted: 24 Apr 2012 11:23
by foxidrive
smccaffr39 wrote:
Squashman wrote:You will need nested for loops. The first one to get a list of the directories, then a 2nd one to then list the files within that directory. I wouldn't use PATH as a variable name in a batch file unless you actually want to change that environmental variable.

Code: Select all

SET sPath=\\server\path
pushd "%sPath%"
FOR /F "delims=" %%I in ('dir /ad /b /s') do (
     pushd "%%~I"
     FOR /F "delims=" %%G IN ('DIR /B /A:-D /O:D') DO SET NewestFile=%%G
     popd
)
popd


thanks Squashman, that's an elegant solution. looks like i'm on my way.


Looking briefly, with the /b it will still give you the most recent file in the last folder.

To solve the issue you would need to get the newest file in each folder, then compare them by date and time to get the newest filename.

Re: Finding newest file in all subdirectories

Posted: 24 Apr 2012 11:29
by Squashman
foxidrive wrote:
smccaffr39 wrote:
Squashman wrote:You will need nested for loops. The first one to get a list of the directories, then a 2nd one to then list the files within that directory. I wouldn't use PATH as a variable name in a batch file unless you actually want to change that environmental variable.

Code: Select all

SET sPath=\\server\path
pushd "%sPath%"
FOR /F "delims=" %%I in ('dir /ad /b /s') do (
     pushd "%%~I"
     FOR /F "delims=" %%G IN ('DIR /B /A:-D /O:D') DO SET NewestFile=%%G
     popd
)
popd


thanks Squashman, that's an elegant solution. looks like i'm on my way.


Looking briefly, with the /b it will still give you the most recent file in the last folder.

To solve the issue you would need to get the newest file in each folder, then compare them by date and time to get the newest filename.

Well it technically does get the newest file in each sub directory is just keep overwriting the variable for the newest file. I have no idea how to compare dates and times in a batch file. I initially thought he wanted the newest file in each sub directory and he was going to do something with that file or echo it to a log file.

Re: Finding newest file in all subdirectories

Posted: 24 Apr 2012 11:43
by Squashman
Should probably ask if you are looking for the newest created file or the newest modified file.

Re: Finding newest file in all subdirectories

Posted: 24 Apr 2012 12:44
by smccaffr39
just to add to this, i am looking for the newest modified in all sub-directories. here's what i've come up with as an end-result.

Code: Select all

@ECHO OFF 
SETLOCAL ENABLEDELAYEDEXPANSION

SET FFPath=\\Server1\Folder1\
SET NewPath=\\Server2\Folder2
REM Arbitrary yyyymmdd starting point, nothing will be older than it
SET NewestDate=20000101

ECHO Recursively searching %FFPath%
echo.

FOR /F "delims=" %%I in ('DIR %FFPath%\C*.rtf /a:-d /s /b') DO (
   SET FullDate=%%~tI
   
   REM Set CurrDate to yyyymmdd format.  Note:  Will fail if regional settings changed.
   SET CurrDate=!FullDate:~6,4!!FullDate:~0,2!!FullDate:~3,2!

   If !CurrDate! gtr !NewestDate! (
      SET NewestDate=!CurrDate!
      SET NewestFile=%%~fI
   )
)

ECHO Copying %NewestFile% to %NewPath%
ECHO.
COPY /Y "%NewestFile%" "%NewPath%"
ECHO.
PAUSE


thanks all!

Re: Finding newest file in all subdirectories

Posted: 24 Apr 2012 12:45
by foxidrive
This uses VBS in WSH to do it. I snaffled the VBS script from google and modified it to provide a listing of each file in a tree in the format
YYYYMMDD-HHMMSS drv:path\filename

So after the VBS script does that (which should work in any region and locale) then the batch file sorts the info and returns the most recent file.

It works here in XP SP3 and uses the date last modified.

Code: Select all

@echo off
if "%~1"=="" (
echo Finds the most recent file in a folder and subdirectories
echo SYNTAX: "%~nx0" [path]
echo where path is the folder tree to check EG "d:\data files"
pause
goto :EOF
)
set "fileout=%temp%\tempfile.tmp"

(
echo Dim strPath
echo Set oFSO = CreateObject^("Scripting.FileSystemObject"^)
echo strPath = "%~1"
echo ' strPath = InputBox^("Enter Folder Path: "^)
echo Set fOut = oFSO.CreateTextFile^("%fileout%", True^)
echo DoStuff oFSO.GetFolder^(strPath^).Path
echo Sub DoStuff^(sDir^)
echo Set oDir = oFSO.GetFolder^(sDir^)
echo For Each i In oDir.Files
echo n = oFSO.GetFile^(sDir + "\" + i.Name^).DateLastModified
echo fOut.WriteLine CStr^(Year^(n^)^) + Right^(100+Month^(n^),2^) + Right^(100+Day^(n^),2^) + "-" + Right^(100+Hour^(n^),2^) + Right^(100+Minute^(n^),2^) + Right^(100+Second^(n^),2^) + " " + sDir + "\" + i.Name
echo Next
echo For Each i In oDir.SubFolders
echo DoStuff i.Path
echo Next
echo End Sub
)>"%temp%\vbsfiledate.vbs"

cscript /nologo "%temp%\vbsfiledate.vbs"
del "%temp%\vbsfiledate.vbs"
sort /r <"%fileout%" > "%fileout%2"

set /p "file=" < "%fileout%2"

for /f "tokens=1*" %%a in ("%file%") do set "file=%%b"

:done

del "%fileout%?"
echo The most recent file in the "%~1" tree is
echo "%file%"

goto :EOF


:: raw VBS script


Dim strPath
Set oFSO = CreateObject("Scripting.FileSystemObject")
strPath = "C:\Program Files"
' strPath = InputBox("Enter Folder Path: ")
Set fOut = oFSO.CreateTextFile("fileout.txt", True)
DoStuff oFSO.GetFolder(strPath).Path
Sub DoStuff(sDir)
Set oDir = oFSO.GetFolder(sDir)
For Each i In oDir.Files
n = oFSO.GetFile(sDir + "\" + i.Name).DateLastModified
fOut.WriteLine CStr(Year(n)) + Right(100+Month(n),2) + Right(100+Day(n),2) + "-" + Right(100+Hour(n),2) + Right(100+Minute(n),2) + Right(100+Second(n),2) + " " + sDir + "\" + i.Name
Next
For Each i In oDir.SubFolders
DoStuff i.Path
Next
End Sub



EDIT: Optimised some lines.

Re: Finding newest file in all subdirectories

Posted: 24 Apr 2012 12:48
by Squashman
smccaffr39 wrote:just to add to this, i am looking for the newest modified in all sub-directories. here's what i've come up with as an end-result.

Code: Select all

@ECHO OFF 
SETLOCAL ENABLEDELAYEDEXPANSION

SET FFPath=\\Server1\Folder1\
SET NewPath=\\Server2\Folder2
REM Arbitrary yyyymmdd starting point, nothing will be older than it
SET NewestDate=20000101

ECHO Recursively searching %FFPath%
echo.

FOR /F "delims=" %%I in ('DIR %FFPath%\C*.rtf /a:-d /s /b') DO (
   SET FullDate=%%~tI
   
   REM Set CurrDate to yyyymmdd format.  Note:  Will fail if regional settings changed.
   SET CurrDate=!FullDate:~6,4!!FullDate:~0,2!!FullDate:~3,2!

   If !CurrDate! gtr !NewestDate! (
      SET NewestDate=!CurrDate!
      SET NewestFile=%%~fI
   )
)

ECHO Copying %NewestFile% to %NewPath%
ECHO.
COPY /Y "%NewestFile%" "%NewPath%"
ECHO.
PAUSE


thanks all!

You should probably use Foxi's code as his takes into account the TIME as well. Your code only looks at the date.

Re: Finding newest file in all subdirectories

Posted: 24 Apr 2012 13:00
by smccaffr39
wow, thanks. that's pretty slick. i'll give it a whirl.

Re: Finding newest file in all subdirectories

Posted: 24 Apr 2012 16:15
by dbenham
An almost identical question was asked recently on StackOverflow: vbscript how to sort files in subfolders by modified date (and prit it with the absolute path of file)

Most solutions are limited to second or minute precision. WMI (WMIC) provides microsecond (10^-6) precision.

Here is a pure batch solution using WMIC that is derived from my answer on SO. It is as accurate as is possible on Windows, and should be fairly bullet proof - no restrictions on file names, etc. It is also not impacted by regional settings.

Code: Select all

:treeNewestFiles ReturnVar [RootFolder]
::
::  Searches the directory tree rooted at RootFolder and saves the
::  most recently modified file in ReturnVar. If RootFolder is not
::  specified then the root is the current directory.
::
@echo off
setlocal disableDelayedExpansion

::define temp folder for temp files
set "tempFolder=%temp%\fildates%random%"
md "%tempFolder%"

::define base path\name for temp files
set "tempFile=%tempFolder%\tempFile.txt"

::Loop through all folders rooted at %2 (current directory if not specified),
::and build a script of WMIC commands that will list last modified timestamps
::and full path of files for each folder. The last modified tamestamp will
::be in ISO 8601 format, so it sorts properly.
(
  echo /append:"%tempFile%1"
  for /r %2 %%F in (.) do (
    set "folder=%%~pnxF"
    set "drive=%%~dF"
    setlocal enableDelayedExpansion
    echo datafile where (path='!folder:\^=\\!\\' and drive='%%~dF'^) get lastmodified, name
    endlocal
  )
  echo quit
)>"%tempFile%"

::Execute the WMIC script
::WMIC creates a temporary file in current directory,
::so change directory first so it doesn't interfere with results.
pushd "%tempFolder%"
cmd /c ^<"%tempFile%" wmic ^>nul 2^>nul

::Convert unicode to ansii
type "%tempFile%1" >"%tempFile%2"

::Preserve only data rows
findstr "^[0-9]" "%tempFile%2" >"%tempFile%3"

::Sort the results in descending order
sort /r "%tempFile%3" >"%tempFile%4"

::Get the most recent file (1st in results)
<"%tempFile%4" set /p "rtn="

::cleanup
popd
rd /q /s "%tempFolder%"

::return result
endlocal & set "%1=%rtn:~27%"

I don't think it is possible to get better than minute precision when searching multiple folders using pure batch unless WMIC is used. The DIR command sorts according to the detailed timestamp associated with the file, but only within a directory. To sort across folders, you must do your own sort, and the DIR and FOR variables only provide access to minute precision.

For what it is worth (not much), here is a non-WMIC batch solution with minute precision that is 2 to 6 times faster than the WMIC solution. It is absolutely dependent on regional settings, but it should be easy to adapt it to what ever regional settings are in effect. It avoids delayed expansion so it can handle names containing ! without SETLOCAL/ENDLOCAL toggling.

This code assumes "MM/DD/YYYY hh:mm AP" format where hh is hour in 12 hour format and AP represents the AM or PM suffix.

Code: Select all

: treeNewestFiles  rtnVar  [RootFolder]
::
::  Searches the directory tree rooted at RootFolder and saves the
::  most recently modified file in ReturnVar. If RootFolder is not
::  specified then the root is the current directory. This algorithm
::  is limited to minute precision.
::
::  Assumes local time stamp format is MM/DD/YYYY hh:mm AP
::  where:
::    MM = month
::    DD = day
::    YYYY = year
::    hh = hour (00-12)
::    mm = minutes
::    AP = AM or PM
::
@echo off
setlocal disableDelayedExpansion
set "tempFile=%temp%\fileDates%random%.txt"

dir /b /s /a-d %2 >"%tempFile%" 2>nul

(
  for /f "usebackq delims=" %%Z in ("%tempFile%") do (
    for /f "tokens=1-6 delims=/: " %%A in ("%%~tZ") do (
      if %%D==12 (echo %%C/%%A/%%B%%F00:%%E %%Z) else echo %%C/%%A/%%B%%F%%D:%%E %%Z
    )
  )
) >"%tempFile%2"

sort /r "%tempFile%2" >"%tempFile%3"

::Get the most recent file (1st in results)
<"%tempFile%3" set /p "rtn="

::cleanup
del "%tempFile%*"

::return result
endlocal & set "%1=%rtn:~18%"


Dave Benham