You could use for /R /D
to recursively walk through all directories, then use dir /B /A:-D /O:-D
to return a list of files sorted per date (newest first) and parse its output by for /F
taking only the first item, so you enumerate only the one newest file in every directory:
@echo off
setlocal EnableExtensions EnableDelayedExpansion
> "C:\results.txt" (
for /D /R %%D in ("C:\srm2\reg\*.*") do (
call :SUB FILE LINE "%%~fD\*.cpf" "ProjectVersion"
if defined LINE (
echo/!FILE!:!LINE!
)
)
)
endlocal
exit /B
:SUB var_file var_line pattern string
set "%~1=" & set "%~2="
for /F "eol=| delims=" %%F in ('dir /B /A:-D /O:-D "%~3"') do (
set "%~1=%%~fF"
for /F "delims=" %%L in ('findstr /L "%~4" "%%~fF"') do (
set "%~2=%%L"
goto :EOF
)
goto :EOF
)
Let us take a look at the subroutine :SUB
first, which is intended to execute once per each directory. This requires 4 parameters:
- (
%~1
) name of variable to hold the path to the retrieved file,
- (
%~2
) name of variable to hold the line that matches the search string,
- (
%~3
) the pattern to get a sorted list of files (newest first), and
- (
%~4
) the search string (expected to occur at most once per file).
The outer for /F
loop parses the output of the command dir /B /A:-D /O:-D "%~3"
, which actually builds the sorted list of files. The loop wants to iterate through every single line, but since there is a goto :EOF
at the end, the loop is broken after the first iteration, so only the first (and therefore the newest) list item is processed. The path to this item is assigned to the first variable given as an argument. If no items match the file pattern, the variable remains empty.
The inner for /F
loop parses the output of the search command findstr
. Since only one occurrence of the search string is expected at most, this loop should iterate once only at most. If a match is found, the containing line is assigned to the second variable given as an argument. If no match is found, the variable remains empty.
Now let us check out the main routine. This walks through the given directory tree recursively using for /D /R
and iterates through all directories. For each one, the subroutine :SUB
is called, passing over the following arguments:
- variable name
FILE
(will receive the newest file in the currently itereted directory),
- variable name
LINE
(will receive the line in the file that matches the search string),
- the currently iterated directory with the file pattern
reg*.cpf
appended, and
- the demanded search string
ProjectVersion
.
Finally, the variable LINE
is checked against emptiness, and if not, a line containing the file path, a colon and the found line is output by echo
.
Notice that all output is redirected into file C:\results.txt
.
This approach using a subroutine has been chosen, because there is a goto
command used to terminate one certain for
loop upon its first iteration (see description above), but not all of them. goto
actually breaks all block contexts it is placed in, even all nested ones. for
loops are just a kind of block command, like if
clauses, or also simple grouped commands ()
. Using a subroutine prevents blocks of the main routine from being broken by a goto
placed in the subroutine.