Search code examples
windowsbatch-filefindstrforfiles

Fix findstr to ignore matching filenames with path


I'm trying to create a list of files created yesterday.

I've got the below working without /c:"cmd /c echo @path" but I want the full path.

forfiles /s /d -1 /m *.txt /c:"cmd /c echo @path" > a.txt
forfiles /s /m *.txt /c:"cmd /c echo @path" > b.txt
FOR /f "delims=; tokens=*" %%X IN ('findstr /v /g:a.txt b.txt') DO (
    echo %%X
)

What is the best way to get around the issue with using findstr with a path containing backslashes? Do I need to replace all the backslashes in the comparison files with some other symbol and then change it back later or is there an easier way to do it in the findstr line?


Solution

  • The findstr command checks the very first search expression and changes to regular expression mode when a meta-character is found or to literal mode otherwise, unless you explicitly predefine the mode by /L (literal) or /R (regular expression).

    But findstr is a nasty beast since there are still some problems even with /L:

    • wrong results may be returned with multiple literal search strings, unless you specify /I to do case-insensitive searches; but this is not a problem here anyway since you are dealing with directory and file names, which are treated case-insensitively by Windows anyway;
    • although in literal mode, escaping of meta-characters like ., [, ], ^, $, \, * and ? still occurs when there is a \ in front; you could just double all \ to work around that;

    So the following code should work in most situations; delayed expansion is enabled herein by cmd /V, which is required to read the interim variable FILE that is written and read in the same command line:

    forfiles /S /M "*.txt" /D -1 /C "cmd /C if @isdir==FALSE (set FILE=@path) & cmd /V /C echo(!FILE:\=\\!" > "exclude.txt"
    forfiles /S /M "*.txt"       /C "cmd /C if @isdir==FALSE echo @path" > "all.txt"
    for /F "delims=" %%X in ('findstr /V /L /I /X /G:"exclude.txt" "all.txt"') do (
        echo(%%X
    )
    

    I inserted if @isdir==FALSE here to not match directories whose names end in .txt. Also I added /X to findstr in order to match whole lines/paths only.

    Regard that literal findstr search strings are limited to a length of 511 bytes (after doubling \), which can easily be reached with file paths.


    However, what about a different approach that avoids findstr at all?

    Here is a post I once provided for returning items newer than a relative date with forfiles: FORFILES date -after- (date calc in cmd file).