Like others, I would use findstr
, because it is a lot faster than using nested for
loops. However, I would turn the thing around and let a list of the actually present files be the search strings and use them to search the input list file upload_filenames.txt
. Though I could not get around one for
loop to derive the pure file names from the file paths. Anyway, here is the code:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=." & rem // (path to target root directory)
set "_MASK=*.*" & rem // (file pattern, usually `*.*` for all)
set "_LIST=%~dp0upload_filenames.txt" & rem // (path to file containing name list)
set "_PASS=%~dp0found.txt" & rem // (path to positive result file)
set "_FAIL=%~dp0missing.txt" & rem // (path to negative result file)
set "_FULL=%~dpn0_all.tmp" & rem // (path to a temporary file)
set "_NAME=%~dpn0_names.tmp" & rem // (path to another temporary file)
rem // Put list of full paths of all files in the target directory tree to a file:
dir /S /B /A:-D "%_ROOT%\%_MASK%" > "%_FULL%"
rem // Reduce list of paths by maintaining the only pure file names:
> "%_NAME%" (
for /F "usebackq delims= eol=|" %%L in ("%_FULL%") do (
echo(%%~nxL
)
)
rem /* Let `findstr` twice do the search, using the file names from the target
rem directory tree as search strings against the original list file: */
findstr /I /X /L /G:"%_NAME%" "%_LIST%" > "%_PASS%"
findstr /I /X /V /L /G:"%_NAME%" "%_LIST%" > "%_FAIL%"
rem // Clean up temporary files:
del "%_FULL%" "%_NAME%"
endlocal
exit /B
Here is a slightly different approach, which returns full paths of the existing files, which becomes important when file names may occur multiple times within the given directory tree:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=." & rem // (path to target root directory)
set "_MASK=*.*" & rem // (file pattern, usually `*.*` for all)
set "_LIST=%~dp0upload_filenames.txt" & rem // (path to file containing name list)
set "_PASS=%~dp0found.txt" & rem // (path to positive result file)
set "_FAIL=%~dp0missing.txt" & rem // (path to negative result file)
set "_AUGM=%_LIST%.tmp" & rem // (path to temporary list file)
set "_FULL=%~dpn0_all.tmp" & rem // (path to a temporary file)
set "_NAME=%~dpn0_names.tmp" & rem // (path to another temporary file)
rem // Create augmented copy of list file with each line preceded by `\\`:
> "%_AUGM%" (
for /F "usebackq delims= eol=|" %%L in ("%_LIST%") do (
echo(\\%%~L
)
)
rem // Put list of full paths of all files in the target directory tree to a file:
dir /S /B /A:-D "%_ROOT%\%_MASK%" > "%_FULL%"
rem // Reduce list of paths by maintaining the only pure file names:
> "%_NAME%" (
for /F "usebackq delims= eol=|" %%L in ("%_FULL%") do (
echo(%%~nxL
)
)
rem /* Let `findstr` do a search, using the augmented list file against the file
rem containing the list of full paths in order to eventually get full paths,
rem which is particularly important if file names are not unique in the tree: */
findstr /I /E /L /G:"%_AUGM%" "%_FULL%" > "%_PASS%"
rem /* Let `findstr` do another search, using the file names from the target
rem directory tree as search strings against the original list file this time: */
findstr /I /X /V /L /G:"%_NAME%" "%_LIST%" > "%_FAIL%"
rem // Clean up temporary files:
del "%_AUGM%" "%_FULL%" "%_NAME%"
endlocal
exit /B
N. B.: Luckily the nasty flaw of findstr
with multiple literal search strings does not apply here, because we are performing case-insensitive searches. Also there is no problem with unintended escaping, because pure file names cannot contain \
, which is the escape character of findstr
.