windowsbatch-filerecursionstackexceed

Recursive search through directory tree for files in date range - getting "Batch Recursion exceeds stack limits" error


I am a newby to dos batch scripts. I pieced together several code snippets, and with a few modifications, got my script to work for a small directory. It recurses through a directory tree, counts the number of files within a date range passed in as parameters, and then writes an output file report.

It works fine for a small directory structure, but if it has to recurse through more than a few hundred folders, then it aborts with the "Batch recursion exceeds stack limits" error.

I understand from this site that recursive loops aren't very efficient and once I've put a certain amount of data on the stack I'm toast. I have looked on this site and elsewhere for help. Most of the advice is to write a more efficient program, but I'm not certain of how to do that. Any help would be appreciated. I need to increase the efficiency by an order of magnitude, as the directory structures I need this for will have thousands of folders. Here is my code:

@echo off
setlocal enableDelayedExpansion
pushd %1

REM This program takes three parameters <starting directory> <startdate> <enddate>
REM The startdate and endate should be in format: mm/dd/yyyy
REM The program will recursively look through all directories and sub-directories
REM from the given <starting directory> and count up the number of files written
REM within the date range from <startdate> until <endate>
REM It will then write out a RetentionReport_<date>_<time>.txt file that lists
REM one line showing the <startdate> <enddate> and the # of files found.

REM If you don't pass in all three arguments it will let you know and then exit.

REM You need to set your TESTDIR below to a hardpath location for the writing
REM of temporary files and writing of the Reports

REM There is one .tmp file created during processing and then deleted.
REM To prevent the .tmp file being deleted you can comment out the second
REM instance of this line by adding a REM in front of it:
REM if exist %TESTDIR%*.tmp del %TESTDIR%*.tmp

REM If you want to print out a .tmp file that lists the files counted for the
REM period given then you could remove the REM from in front of the following
REM line in the below code: echo %%~tF   %%F>> %TESTDIR%MONTH_files.tmp


set "TAB=   "
set "MONTHTOTAL=0"
set hr=%time:~0,2%
if "%hr:~0,1%" equ " " set hr=0%hr:~1,1%
set "TESTDIR=C:\TEST\"

if "%~2" == "" (
    echo Please pass in arguments for starting directory, startdate, and enddate.
    echo startdate and endate should be in the format mm/dd/yyyy
    exit/b
)
if "%~3" == "" (
    echo Please pass in arguments for starting directory, startdate, and enddate.
    echo startdate and endate should be in the format mm/dd/yyyy
    exit/b
)


set "startdate=%~2"
set "enddate=%~3"

if exist %TESTDIR%*.tmp del %TESTDIR%*.tmp
call :run >nul
FOR /F %%i IN (%TESTDIR%temp_TotalByDir.tmp) DO set /a MONTHTOTAL=!MONTHTOTAL!+%%i
echo %startdate%%TAB%%enddate%%TAB%!MONTHTOTAL! >> %TESTDIR%RetentionReport_%date:~-    4,4%%date:~-10,2%%date:~-7,2%_%hr%%time:~3,2%%time:~6,2%.txt
if exist %TESTDIR%*.tmp del %TESTDIR%*.tmp
exit /b

:run
for %%F in (.) do echo %%~fF
endlocal

:listFolder
setlocal enableDelayedExpansion
set "ADD=0"

for %%F in (*) do ( 
  if %%~tF GEQ %startdate% (
      if %%~tF LEQ %enddate% (
          REM echo %%~tF   %%F>> %TESTDIR%MONTH_files.tmp
      set /a ADD+=1
      )
  )
)

echo !ADD! >> %TESTDIR%temp_TotalByDir.tmp

for /d %%F in (*) do (
  pushd "%%F"
  call :listFolder
  popd
)

endlocal

exit /b

Thanks in advance for your help!!!


Solution

  • You have other problems besides recursion stack limits. The IF statement does not know how to compare dates. It only knows strings and numbers. In order to properly compare dates you must reformat the date to be in YYYYMMDD format.

    Your timestamp information for use in the output file name should be collected all at once. Your existing code gets the hour at the beginning of the process, and the date, minutes and seconds at the end of the process. Not good. There could be a significant time gap between the start and end times.

    Batch has two types of recursion errors:

    1) Only 31 SETLOCAL levels within one CALL level.

    2) A variable number of recursive CALLs allowed, depending on Windows version, machine memory, ...

    There is no way to increase the size of the stack. If you get a recursion error, you must look for ways to reduce the amount of recursion. In your case, you can simply let FOR /R do all the recursion for you!

    I modified the script such that the list of files is generated if a 4th argument is passed. I also incorporated the timestamp into file list file name.

    The code assumes your machine's file date/time value starts with MM/DD/YYYY. The code will have to be modified if it does not.

    @echo off
    setlocal enableDelayedExpansion
    
    REM This program takes three parameters <starting directory> <startdate> <enddate>
    REM The startdate and endate should be in format: mm/dd/yyyy
    REM The program will recursively look through all directories and sub-directories
    REM from the given <starting directory> and count up the number of files written
    REM within the date range from <startdate> until <endate>
    REM It will then write out a RetentionReport_<date>_<time>.txt file that lists
    REM one line showing the <startdate> <enddate> and the # of files found.
    
    REM If you don't pass in all three arguments it will let you know and then exit.
    
    REM You need to set your TESTDIR below to a hardpath location for the writing
    REM of the Reports
    
    REM If you want to print out a .tmp file that lists the files counted for the
    REM period given then you can pass in a 4th argument with any value
    
    if "%~3" == "" (
      echo Please pass in arguments for starting directory, startdate, and enddate.
      echo startdate and endate should be in the format mm/dd/yyyy
      exit/b
    )
    
    set "TAB=   "
    set "TESTDIR=D:\TEST\"
    
    set "startdate=%~2"
    set "start=%startdate:~-4%%startdate:~0,2%%startdate:~3,2%"
    set "enddate=%~3"
    set "end=%enddate:~-4%%enddate:~0,2%%enddate:~3,2%"
    
    set "timestamp=%date:~-4%%date:~-10,2%%date:~-7,2%_%time:~0,2%%time:~3,2%%time:~6,2%"
    set "timestamp=%timestamp: =0%"
    
    for /r "%~1" %%F in (*) do (
      set "dt=%%~tF"
      set "dt=!dt:~6,4!!dt:~0,2!!dt:~3,2!"
      if !dt! geq %start% if !dt! leq %end% (
        if "%~4" neq "" (echo %%~tF   %%F) >>"%TESTDIR%MONTH_files_%timestamp%.tmp"
        set /a cnt+=1
      )
    )
    
    (echo %startdate%%TAB%%enddate%%TAB%%cnt%) >>"%testdir%RetentionReport_%timestamp%.txt"