I need a Windows batch code to find the last modified file among all the files in all the subfolders of a specific folder.
It doesn't seem to be so simple as it appears.
Some files could have been modified in the same minute, but in different seconds. I need only the lastest one.
#echo on
setlocal enableextensions disabledelayedexpansion
set "root=c:\somewhere"
for %%r in ("%root%\.") do for /f "tokens=3,*" %%a in ('
robocopy "%%~fr." "%%~fr." /l /nocopy /s /is /njh /njs /ndl /nc /ns /ts
^| sort /r
^| findstr /n "^"
^| findstr /l /b /c:"1:"
') do echo %%b
This code uses robocopy (native for Vista and later OS versions, downloadable from Microsoft for XP or 2003) to obtain a list of files with timestamp in yyyy-mm-dd hh:nn:ss format, that is sorted in decreasing order and then only the first line (that is, the newer file) is retrieved.
Related
I wrote a batch file which should find and copy the latest build file bigger than a 3.5 GB.
set source=D:\src
set tmp_dir="D:\tmp"
set "file="
for /f "delims=|" %%a in ('
dir /b /o-d "%source%\*.zip" 2^>nul
^| cmd /q /v /c"set /p .=&if defined . (echo(!.!)"
') do set "file=%%a"
echo %file%
robocopy %source% %tmp_dir% %file% /MT:16 /X /MIN:3500000000 /XO /V /TS /FFT /R:3 /W:10 /ETA
My problem is what happening when the latest file is smaller than 3.5 GB. In this case is need to copy the next file larger than 3.5 GB.
You almost have it. Instead of using the dir command to retrieve the initial list of files, we can use robocopy to get a list (/l) of files in the source folder with the required minimum size (/min). This list should not include job header, job footer, nor directory list (/njh, /njf, /ndl). For each file we don't need the class of the file (/nc) or the size of the file (/ns), but we want the time stamp (/ts) of the files to sort the list on this field.
This will leave a sorted list of files with the newer file matching the conditions as the first in the list.
#echo off
setlocal enableextensions disabledelayedexpansion
set "source=D:\src"
set "tmp_dir=D:\tmp"
set "file="
for /f "tokens=2,*" %%a in ('
robocopy "%source%" . *.zip /l /xx /is /njh /njs /ndl /nc /ns /ts /min:350000000000
^| sort /r
^| cmd /q /v /c"set /p .=&if defined . (echo(!.!)"
') do set "file=%%~nxb"
echo(%file%
if defined file (
robocopy "%source%" "%tmp_dir%" "%file%" /MT:16 /X /XO /V /TS /FFT /R:3 /W:10 /ETA
)
You can get the size of a file in bit using the following line
for /f "usebackq" %%a in ('%file%') d set filesize=%%~za
Add
set /a filesize+=0
to make sure it is treated like a numeric variable.
Then use an if-statement to check if filesize exceeds 3500000000 byte -> 3.5 GB.
if filesize LSS 3500000000 (
echo not 3.5 GB
) ELSE (
echo bigger than 3.5 GB
)
Adding other echoes is up to you.
I have files named file.txt in multiple subfolders in my folder. I want to get the path of the latest file.txt
FOR /r %%i IN ('DIR file.txt /B /O:-D') DO SET a=%%i
echo Most recent subfolder: %a%
gives latest created folder having file.txt whereas I want the folder which has latest file.txt
#echo off
setlocal enableextensions disabledelayedexpansion
for /f "tokens=1,2,*" %%a in ('
robocopy . . file.txt /l /nocopy /is /s /nc /ns /ts /ndl /njh /njs
^| sort /r 2^> nul
^| cmd /v /q /c "set /p .=&echo(^!.^!"
') do (
echo File found : %%c
echo File timestamp (UTC^) : %%a %%b
echo Folder of file : %%~dpc
)
This will use the robocopy command to enumerate all the file.txt files under the current active directory, without copying anything but generating a list of all matching files with a yyyy/mm/dd hh:nn:ss utc timestamp. Then the list is sorted on the timestamp in descending order and only the first line in the output readed and echoed to be processed by the for /f tokenizer and retrieve the date, time and file with full path.
If only the folder is required and the list of files is not very large, a simplified version could be
#echo off
setlocal enableextensions disabledelayedexpansion
for /f "tokens=1,2,*" %%a in ('
robocopy . . file.txt /l /nocopy /is /s /nc /ns /ts /ndl /njh /njs
^| sort /r
') do set "lastFolder=%%~dpc" & goto :done
:done
echo Last folder : %lastFolder%
Almost the same, but instead of including a filter in the list generation to only retrieve the first line (required if the list of files is very large), here the for /f will retrieve the full list but after the first element is processed we jump out of the loop to the indicated label.
I had the below code for searching through sub directories for 2 exe files:
#echo off & setLocal EnableDELAYedeXpansion
for %%d in (c) do if exist %%d: (
for /f "delims=" %%a in ('dir/b/s/x %%d:\autolog.exe %%d:\autorun.exe 2^>nul ^| findstr /V /C:".*\.*\.*\.*\.*\.*\.*\.*\.*" /C:".*\.*\.*\.*\.*\.*\.*\.*" /C:".*\.*\.*\.*\.*\.*\.*" /C:".*\.*\.*\.*\.*\.*" /C:".*\.*\.*\.*\*"') do (
set var=%%a;!var!
))
echo %1,!var!, >>C:\test.txt
exit
While it works search for all subfolder (by using /s), I would like to have result returns only if it is within 4 subfolder level (e.g. c:\sf1\sf2\sf3\autorun.exe should be a valid result, while c:\sf1\sf2\sf3\sf4\autorun.exe and any finding further down the tree should be opt out and not returning as a result).
I use all wildcard combination (* | .| .*) along with "\V" in attempt to achieve it but failed. Why does it won't work or if there are other smarter way doing it?
Thanks in advance
Heres is a sample to limit to fourth folder level, using regular expressions in the findstr terms:
#echo off
for /f "delims=" %%a in ('dir /ad /b /s ^| findstr \\.*\\.*\\.*\\ ^| findstr /v \\.*\\.*\\.*\\.*\\') do echo %%a
pause
I was inspired by this answer. ROBOCOPY is available since Vista, and it is a robust utility that does more than copying files.
e.g. The /L switch prevents it from copying; while /LEV allows you to copy only the top N levels of root, which eliminates one FINDSTR.
Golfed
#echo off
SETLOCAL EnableDelayedExpansion
FOR /F "tokens=*" %%F in ('
ROBOCOPY "C:\." "C:\." autolog.exe autorun.exe /S /LEV:4 /IS /L /NS /NC /NDL /NJH /NJS^|FINDSTR \\.*\\.*\\.*\\
') do set "var=%%~fF;!var!"
echo %1,!var!,>>C:\test.txt
Formatted
#echo off
====SETLOCAL EnableDelayedExpansion EnableExtensions
set "list="
set ^"FORCMD=^
%__APPDIR__%ROBOCOPY.EXE C:\. C:\. autolog.exe autorun.exe^
/S %=non-empty Subdirectories=%^
/LEV:4 %=MAX 4 LEVels=%^
/IS %=Include Same files=%^
/L %=List only (don't copy)=%^
/NS %=No Size=%^
/NC %=No Class=%^
/NDL %=No Directory List=%^
/NJH %=No Job Header=%^
/NJS %=No Job Summary=%^
|%__APPDIR__%FINDSTR.EXE \\.*\\.*\\.*\\%=MIN 4 LEVels=%" It's convenient to use delayed expansion
FOR /F "tokens=*" %%F in ('!FORCMD!') do set "var=%%~fF;!var!"
::Due to weird expansion rules
::if VAR was undefined, it is set to '~,-1'
if DEFINED var set "var=!var:~,-1!" Remove trailing ;
>>"C:\test.txt" echo(%1,!var!,
I am a complete novice in batch programming but have found some great scripts in here that I tried modifying. I need the info on the last file modified in a directory. The script below gives me a file with info about file name and modification time. It searches through subdirectories too but seems to get stuck in a subdirectory instead of finding the newer file in the parent directory. I am not sure what could be wrong (as I only partly understand the code). Any suggestions from you smart guys in here?
Thanks in advance!
#echo off
setlocal
set srcDir=C:\Test
set lastmod=
pushd "%srcDir%"
for /f "tokens=*" %%a in ('dir *. * /b /od /s /a-d 2^>NUL') do set lastmod=%%a
if "%lastmod%"=="" echo Could not locate files.&goto :eof
for /d %%a in ("%lastmod%") do echo "%lastmod%", Modified date: %%~ta>"C:\Test\Details.txt"
This uses robocopy so it will only work on windows Vista and later. For it to work on XP you will need to get a copy of robocopy from a later OS or from resource kit.
No copy operation will really be made, but it will allow to retrieve a recursive file list with an adequated file stamp that can be sorted to find latest file.
#echo off
setlocal enableextensions disabledelayedexpansion
set "folder=%cd%"
for /f "tokens=2,*" %%a in (
'robocopy "%folder%" "%folder%" "*" /s /is /nocopy /nc /ns /ts /fp /np /ndl /njh /njs /xjd /r:0 /w:0 /l ^| sort /r '
) do ( set "latest=%%b" & goto :done )
:done
for %%f in ("%latest%") do echo(%%~tf %%~ff
I am working on renaming files that come from digital camera video recordings.
They output files in a generic incremental filename of which I do not need any information from. My folder structure is: x:\processing\01020304\VIDEO\0001.avi.
The filename I am trying to achieve would be for this example:
01-02-20121227-07h30m00s-03-04.avi
In this example, 01-02 is the first 4 numbers of the 01020304 folder. 20121227 is the date the video was created, with 07h30m00s as the ending time of the video, which is usually reported by the "last modified time". finally the 03-04 is the second half of the folder name used previously.
I'm a complete novice to writing batch files like this, but was unable to find any other examples similar enough to get myself started, so any help would be greatly appreciated.
edit:
so far I've got:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
:: Setup
set "xPath=c:\processing\01010101\VIDEO"
set "xFolder="
set "xDateCreated="
:: Parse Folder Name
for /f "tokens=3 delims=\" %%A in ("%xPath%") do set "xFolder=%%A"
:: Loop through Videos
pushd %xPath%
for /f "tokens=1,2,3,4,*" %%A in ('dir *.avi /T:W /4') do if exist "%%~fE" (
set "xDateCreated=%%~A %%~B %%~C"
echo %xFolder:~0,2%-%xFolder:~2,2%-!xDateCreated:~6,4!!xDateCreated:~0,2!!
xDateCreated:~3,2!-!xDateCreated:~11,2!h-!xDateCreated:~14,2!m-00s-%xFolder:~4,2%-%
xFolder:~6,2%%%~xE
)
popd
endlocal
pause
we're almost there, just missing the seconds, and if possible id like to change it so i do not have to supply the directory, but rather run it from the processing folder and check all 01010101, 02010101, etc directories..
Updated Answer
#echo off
setlocal EnableExtensions EnableDelayedExpansion
:: Create Empty Folder
rd /Q "%Temp%\Temp" 2>nul & mkdir "%Temp%\Temp"
:: Loop through Folders
pushd "xPath=x:\processing"
for /d %%D in (*) do call :Process "%%~fD"
popd
goto End
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:Process <Parent>
:: Folder Name
set "xFolder=%~nx1"
:: Set Sub Folder
if not exist "%~1\VIDEO\" goto :eof
pushd "%~1\VIDEO"
:: Loop through Videos
for /f "delims=" %%A in ('dir *.avi /b') do if exist "%%~fA" (
set "xDateWritten=%%~tA"
set "xDateGMT=0000/00/00 00:00:00"
for /f "tokens=1,2" %%X in ('robocopy . "%Temp%\Temp" "%%~nxA" /TS /FP /NS /NC /NP /NJH /NJS /NDL /L') do set "xDateGMT=%%X %%Y"
rem Format = FF-FF-YYYYMMDD-HHh-MMm-SSs-FF-FF.ext
echo %xFolder:~0,2%-%xFolder:~2,2%-!xDateWritten:~6,4!!xDateWritten:~0,2!!xDateWritten:~3,2!-!xDateWritten:~11,2!h-!xDateWritten:~14,2!m-!xDateGMT:~17,2!s-%xFolder:~4,2%-%xFolder:~6,2%%%~xA
)
popd
goto :eof
:End
endlocal
pause
Since you changed the T:C to T:W I adjusted the script to just use the Last Written Date and I added the folder recursion.
When ready replace the echo line with this:
ren "%%~fA" "%xFolder:~0,2%-%xFolder:~2,2%-!xDateWritten:~6,4!!xDateWritten:~0,2!!xDateWritten:~3,2!-!xDateWritten:~11,2!h-!xDateWritten:~14,2!m-!xDateGMT:~17,2!s-%xFolder:~4,2%-%xFolder:~6,2%%%~xA"
Original & Edits
Well to keep the script simple I am basing the formatting strictly off the folders structure you posted. This is just a skeleton script to get you started. It works, but does not get the length of the video. The length can be calculated based on the information already retrieved, but I did not feel like doing date math right now. :)
#echo off
setlocal EnableExtensions EnableDelayedExpansion
:: Setup
rd /Q "%Temp%\Temp" & mkdir "%Temp%\Temp" 2>nul
set "xPath=x:\processing\01020304\VIDEO"
set "xFolder="
:: Parse Folder Name
for /f "tokens=3 delims=\" %%A in ("%xPath%") do set "xFolder=%%A"
:: Loop through Videos
pushd %xPath%
for /f "tokens=1,2,3,4,*" %%A in ('dir *.avi /T:C /4') do if exist "%%~fE" (
set "xDateWritten=%%~tE"
set "xDateCreated=%%~A %%~B %%~C"
set "xDateGMT=0000000000000000000"
for /f "tokens=1,2" %%X in ('robocopy . "%Temp%\Temp" "%%~E" /TS /FP /NS /NC /NP /NJH /NJS /NDL /L') do set "xDateGMT=%%X %%Y"
echo %xFolder:~0,2%-%xFolder:~2,2%-!xDateCreated:~6,4!!xDateCreated:~0,2!!xDateCreated:~3,2!-!xDateWritten:~11,2!h-!xDateWritten:~14,2!m-!xDateGMT:~17,2!s-%xFolder:~4,2%-%xFolder:~6,2%%%~xE
)
popd
endlocal
pause
Unfortunately, the t option and dir command do not provide the date resolution needed for seconds. To obtain the full last modified time stamp of the file you can use either the forfiles or robocopy commands.
rd /Q "%Temp%\Temp" 2>nul & mkdir "%Temp%\Temp" && robocopy . "%Temp%\Temp" *.avi /TS /FP /NS /NC /NP /NJH /NJS /NDL /L
NOTE: RoboCopy returns the time stamp of the file in GMT+0! It does not return the time stamp based upon the user's timezone settings. To determine which timezone you are, use tzutil /g.
See RoboCopy /? (Vista+ or XP Resource Kit)
forfiles /M *.avi /C "cmd /c echo #ftime"
See ForFile /? (Vista+)
Edit:
I updated the code to include the last written hour and minute into the file name. All that needs to be done is to add the rename command in the loop. To obtain the seconds for the time stamp from the files, either the robocopy or forfiles commands will have to be used.
Edit 2:
Added note about RoboCopy and the GMT+0 file time stamp and updated code to work with this limitation. Added the rd and mkdir to ensure that we have an empty folder, Added the for parse for the robocopy file timestamp, and added the xDateGMT variable. This should fully work now, if I have time later, I will clean it up a little more.
Edit 3:
Reformatted the answer to make it cleaner. See above Update section.