-2

How do I get "FINDSTR" find a value between a specified range ? I set the "string": "20141001" and "20141030" . If within the *.TXT file exists "20141017", should return the "ERRORLEVEL" = 0.

EXEMPLE:

@echo off

SET DATE_STA=20141001
SET DATE_END=20141030

echo Looking for all the files in the folder
echo within the range from %DATE_STA% to %DATA_END% ...
echo.

:FINDING
findstr /r "%DATE_STA% to %DATA_END%" C:\Folder\*.txt
IF %ERRORLEVEL%==0 (
goto OKAY) else (
goto FAIL
)

:OKAY
cls
echo.
echo Located file that contains a value in the specified range.
echo.
pause
exit

:FAIL
cls
echo.
echo Any file located in this folder..
echo.
pause
exit
  • I don't see you using `FINDSTR` anywhere in your batch file. Please also provide research you've already done to get to where you are. – zealoushacker Oct 16 '15 at 19:47
  • `findstr` is not capable of doing numeric checks, it searches *strings* within others... – aschipfl Oct 16 '15 at 20:22
  • 1
    `findstr` command can _not_ find strings "between specified ranges". Each string must be tested individually to do that: `if !value! geq %DATE_STA% if !value! leq %DATA_END% goto OKAY`, but we need to know the format of the files in order to write a solution. You may use `findstr /R "201410.."` to find date strings corresponding to October/2014, but this returns _complete lines_ that contain such dates and _not_ just the date strings! Unless, of course, that each file line contain just a date (we need to know the format of the files)... – Aacini Oct 17 '15 at 01:36

2 Answers2

2
  • Use findstr with a regexp that catches only the strings you want:

    findstr /r "\<201410[0-3][0-9]\>"
    

    Of course this is a simplified regexp that will also catch 31 of October (which is bad) and the invalid 00 and 39 (but if your file contains only valid dates it's not a problem), so you'll have to write several regexps for each 10 day range.

  • Or generate a list of the dates in a loop, write them to a file and use that file in findstr. Here's an example that generates two date scopes: 20141001 20141030 and 20151001 20151030:

    @echo off
    del "%temp%\datespan.txt" >nul 2>&1
    call :makeDates 20141001 20141030 "%temp%\datespan.txt"
    call :makeDates 20151001 20151030 "%temp%\datespan.txt"
    findstr /g:"%temp%\datespan.txt" /s C:\Folder\*.txt
    del "%temp%\datespan.txt"
    pause
    exit /b
    
    :makeDates
        setlocal enableDelayedExpansion
        set "date1=%1" & set "date2=%2" & set "dateFile=%3"
        set "y1=!date1:~0,4!" & set "m1=1!date1:~4,2!" & set "d1=1!date1:~6,2!"
        set "y2=!date2:~0,4!" & set "m2=1!date2:~4,2!" & set "d2=1!date2:~6,2!"
        set /a m1-=100, d1-=100, m2-=100, d2-=100
        call :dateCalcLeap & call :dateCalcMonth
        :dateNext
            set "m=0!m1!" & set "d=0!d1!" & set "ymd=!y1!!m:~-2!!d:~-2!"
            if !ymd! GTR !date2! endlocal & exit /b
    
            echo !ymd!>>!dateFile!
    
            set /a d1+=1 & if !d1! GTR !mDays! (
                set "d1=1" & set /a m1+=1 & call :dateCalcMonth
                if !m1! GTR 12 set "m1=1" & set /a y1+=1 & call :dateCalcLeap
            )
            goto dateNext
        :dateCalcMonth
            if !m1!==2 (set/a mDays=28+leapYear) else (set/a mDays="31-(m1-1) %% 7 %% 2")
            exit /b
        :dateCalcLeap
            set leapYear=0
            set /a y4=y1 %% 4 & if !y4!==0 (
                set /a y100=y1 %% 100 & if not !y100!==0 set leapYear=1
                set /a y400=y1 %% 400 & if !y400!==0 set leapYear=1
            )
            exit /b
    

    The above solution will [erroneously] catch the numbers inside other bigger numbers like 22222220141001 so if this is undesirable here's a much slower but more reliable version:

    @echo off
    del "%temp%\datespan.txt" >nul 2>&1
    call :makeDates 20141001 20141030 "%temp%\datespan.txt"
    call :makeDates 20151001 20151030 "%temp%\datespan.txt"
    findstr /g:"%temp%\datespan.txt" /s C:\Folder\*.txt
    del "%temp%\datespan.txt"
    pause
    exit /b
    
    :makeDates
        setlocal enableDelayedExpansion
        set "date1=%1" & set "date2=%2" & set "dateFile=%3"
        set "y1=!date1:~0,4!" & set "m1=1!date1:~4,2!" & set "d1=1!date1:~6,2!"
        set "y2=!date2:~0,4!" & set "m2=1!date2:~4,2!" & set "d2=1!date2:~6,2!"
        set /a m1-=100, d1-=100, m2-=100, d2-=100
        call :dateCalcLeap & call :dateCalcMonth
        :dateNext
            set "m=0!m1!" & set "d=0!d1!" & set "ymd=!y1!!m:~-2!!d:~-2!"
            if !ymd! GTR !date2! endlocal & exit /b
    
            echo \^<!ymd!\^>>>!dateFile!
    
            rem The next three lines catch embedded dates like abc20141001, 10_20141001_22
            echo [^^^^0-9]!ymd![^^^^0-9]>>!dateFile!
            echo [^^^^0-9]!ymd!\^>>>!dateFile!
            echo \^<!ymd![^^0-9]>>!dateFile!
    
            set /a d1+=1 & if !d1! GTR !mDays! (
                set "d1=1" & set /a m1+=1 & call :dateCalcMonth
                if !m1! GTR 12 set "m1=1" & set /a y1+=1 & call :dateCalcLeap
            )
            goto dateNext
        :dateCalcMonth
            if !m1!==2 (set/a mDays=28+leapYear) else (set/a mDays="31-(m1-1) %% 7 %% 2")
            exit /b
        :dateCalcLeap
            set leapYear=0
            set /a y4=y1 %% 4 & if !y4!==0 (
                set /a y100=y1 %% 100 & if not !y100!==0 set leapYear=1
                set /a y400=y1 %% 400 & if !y400!==0 set leapYear=1
            )
            exit /b
    
wOxxOm
  • 65,848
  • 11
  • 132
  • 136
  • Yep, the *simple* regexp is insufficient. I've added another solution. – wOxxOm Oct 16 '15 at 23:58
  • Both "solutions" will fail if the number (date) is embedded within a larger number. For example, 120141001 will give a false positive. – dbenham Oct 20 '15 at 03:50
  • @dbenham, I've fixed both by using `\<` and `\>`. Thanks for the observation! – wOxxOm Oct 20 '15 at 06:13
  • Yes, that is a significant improvement. I would never use the first option because of the false positives you have noted. But the last option should work, albeit via fairly complex code. However, it may still fail if short file names are enabled due to a FINDSTR [BUG - Short 8.3 filenames can break the /D and /S options](http://stackoverflow.com/a/8844873/1012053). But this solution shows good creativity staying within the bounds of pure native batch. – dbenham Oct 20 '15 at 12:07
  • You could avoid the FINDSTR bug by using the FOR /R with TYPE and pipe trick I used in my JREPL solution. – dbenham Oct 20 '15 at 12:12
2

I'm not aware of a good FINDSTR solution. But there is a simple solution using JREPL.BAT - a regular expression text processing utility that runs natively on any Windows machine from XP onward. It is pure script (hybrid batch/JScript) that does not require any 3rd party executables.

The solution uses a simple regular expression, coupled with a tiny bit of custom JScript code provided on the command line.

for /r %%F in (.) do @type "%%F\*.txt" 2>nul | jrepl "\d{8,}" "($0>=20141001 && $0<=20141030) ? $0 : false" /jmatch >nul && echo FOUND || echo NOT FOUND
dbenham
  • 127,446
  • 28
  • 251
  • 390
  • This will also catch `abc20141001xyz` which is probably a good thing. This behavior can be simulated by `findstr` too by adding `[^0-9]20141001[^0-9]`, `[^0-9]20141001\>`, `\<20141001[^0-9]` to the strings file though in addition to `\<20141001\>`, so `findstr` can also process this case, thus being an equally workable solution which will still have the benefit of being fully embeddable (although of course JREPL is a very good thing to have on a pc). – wOxxOm Oct 20 '15 at 06:44
  • It should also be noted that in case there are many files with *huge* amount of numbers that are not dates (like `1`, `55` etc) this solution as is will be slower than findstr because it executes the provided comparison code on each number. It's probably easy to fix by using a more sophisticated regexp. – wOxxOm Oct 20 '15 at 06:56
  • @wOxxOm - The invocation of the custom JScript is really fast, and a more complex regex would require more processing of its own. I'm not sure the extra regex processing would be any faster than the simple numeric comparisons in my JScript. But I did go ahead and edit the regex to exclude numbers that are less than 8 digits. – dbenham Oct 20 '15 at 11:30
  • Any user code will require a sandbox to execute it in an interpreter which adds overhead to invoke whereas regexps are handled internally in the native code. The change you've just made cuts the time to process 18MB log file from ~6 seconds to ~3 here on i7. The naive `findstr` solution in my answer with plain strings takes 0.03 sec (30 milliseconds, 100 times faster), the multi-string regexp findstr in my answer is ridiculously slow and takes more than a minute (I've stopped it). – wOxxOm Oct 20 '15 at 11:50
  • EDIT - I modified the code to handle the recursive directory search that I missed upon my initial readings of the question. – dbenham Oct 20 '15 at 12:09