CMD Script on Windows causes Batch Recursion error and aborts

Hi Guys

I've created a script that emails users when a file in a directory is more than 20 minutes old.  The script works really well, but after running for about 24 hours or so, it errors out with the following error :
******  B A T C H   R E C U R S I O N  exceeds STACK limits ******
Recursion Count=428, Stack Usage=90 percent
******       B A T C H   PROCESSING IS   A B O R T E D      ******
and I have to restart the script again.

The script looks at the spool directory on the local windows machine, and compares the creation date of the file against the system time.  If there is no file that is older than 20 minutes, then the script sleeps for 1 minute.  If there is a file older than 20 minutes, the script uses the vbscript to pull information about the print queue, and uses a program called "gbmail" to send an email to a distribution list (contained in emails.txt) with the output from the vbscript and then sleeps for 5 minutes before going back to the top of the script.

The following is the script (I modified slightly it to remove security information)

@echo off
:: Wmic removes regional differences - it has problems with commas in filenames.
:top
@echo iteration

setlocal

for /f "tokens=2 delims==" %%a in ('wmic OS Get localdatetime /value') do set "dt=%%a"
set "YY=%dt:~2,2%" & set "YYYY=%dt:~0,4%" & set "MM=%dt:~4,2%" & set "DD=%dt:~6,2%"
set "HH=%dt:~8,2%" & set "Min=%dt:~10,2%" & set "Sec=%dt:~12,2%"

set "stamp=%YYYY% %MM% %DD% %HH% %Min%"

call :DateToMinutes %stamp% NowMins

for /f "delims=" %%a in ('dir C:\Windows\System32\spool\PRINTERS\*.SPL /a-d /b') do call :CheckMins "%%a"
goto :Restart

:CheckMins
::echo checkmins
set "filestamp="
set "filemins="
set "MinsOld="
set "YY=" & set "YYYY=" & set "MM=" & set "DD="
set "HH=" & set "Min=" & set "Sec=" & set "dt="
set "file=%~1"
set "filea=%file:\=\\%"
WMIC DATAFILE WHERE name="C:\\Windows\\System32\\spool\\PRINTERS\\%file%" get lastmodified | find "." >file.tmp
for /f %%a in (file.tmp) do set "dt=%%a"
set "YY=%dt:~2,2%" & set "YYYY=%dt:~0,4%" & set "MM=%dt:~4,2%" & set "DD=%dt:~6,2%"
set "HH=%dt:~8,2%" & set "Min=%dt:~10,2%" & set "Sec=%dt:~12,2%"
set "filestamp=%YYYY% %MM% %DD% %HH% %Min%"
::echo %filestamp%
del file.tmp 2>nul

if not defined yyyy goto :EOF

call :DateToMinutes %filestamp% FileMins

set /a MinsOld=%NowMins%-%FileMins%
::echo Now:%NowMins% File:%FileMins% Fileage:%minsold% "%~1"
if %MinsOld% gtr 20 goto DoThis
goto :Restart

:DateToMinutes
setlocal
set yy=%1&set mm=%2&set dd=%3&set hh=%4&set nn=%5
if 1%yy% LSS 200 if 1%yy% LSS 170 (set yy=20%yy%) else (set yy=19%yy%)
set /a dd=100%dd%%%100,mm=100%mm%%%100
set /a z=14-mm,z/=12,y=yy+4800-z,m=mm+12*z-3,j=153*m+2
set /a j=j/5+dd+y*365+y/4-y/100+y/400-2472633
if 1%hh% LSS 20 set hh=0%hh%
if /i {%nn:~2,1%} EQU {p} if "%hh%" NEQ "12" set hh=1%hh%&set/a hh-=88
if /i {%nn:~2,1%} EQU {a} if "%hh%" EQU "12" set hh=00
if /i {%nn:~2,1%} GEQ {a} set nn=%nn:~0,2%
set /a hh=100%hh%%%100,nn=100%nn%%%100,j=j*1440+hh*60+nn
endlocal&set %6=%j%&goto :EOF

:DoThis
::echo oldfile "%file%"
::c:\batch\sysinternals\handle %file%
for /f "tokens=1,2,3,4* delims=. " %%a in ("%file%") do set spoolno=%%a
set spoolno=%spoolno:0=%
cscript C:\Windows\System32\Printing_Admin_Scripts\en-US\prnjobs.vbs -l -j %spoolno% >c:\batch\spoolfile.txt

for /f "tokens=*" %%a in (emails.txt) do C:\batch\gbmail.exe -to %%a -h smtp.host.com -from sender@host.com -s "%file% has been in the system for %MinsOld% minutes on %COMPUTERNAME%" -file spoolfile.txt >nul
::After emailing - sleep for 5 minutes
c:\batch\sleep 300

:Restart
c:\batch\sleep 60
Goto :top

Open in new window


I'd really appreciate it if someone could point me to a solution to prevent the recursion from happening.  Thankyou in advance !
LVL 1
altquarkAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Bill PrewIT / Software Engineering ConsultantCommented:
What is the name of the BAT file you are running and posted here?

~bp
Bill PrewIT / Software Engineering ConsultantCommented:
Looking a little further, my initial thought would be that you are not returning from the "call :CheckMins" inside a FOR loop.  Rather you use GOTO statements during that call to restart the script at the top, and enter the FOR loop again.  This likely leaves the prior FOR loop waiting for the "call :CheckMins" toi return, which it never does.  Over time these pile up until it aborts.

I'll look at what the code is doing a little more and try to make a recommendation to correct.

~bp
altquarkAuthor Commented:
Hi bill - the file name can be anything.  Currently it's called "testit.bat" !
C++ 11 Fundamentals

This course will introduce you to C++ 11 and teach you about syntax fundamentals.

Bill PrewIT / Software Engineering ConsultantCommented:
Give this a try.  I re,moved all GOTO's except the main :TOP loop, and made sure CALLed subroutines returned properly.

@echo off
:: Wmic removes regional differences - it has problems with commas in filenames.

:top
@echo iteration

setlocal

for /f "tokens=2 delims==" %%a in ('wmic OS Get localdatetime /value') do set "dt=%%a"
set "YY=%dt:~2,2%" & set "YYYY=%dt:~0,4%" & set "MM=%dt:~4,2%" & set "DD=%dt:~6,2%"
set "HH=%dt:~8,2%" & set "Min=%dt:~10,2%" & set "Sec=%dt:~12,2%"

set "stamp=%YYYY% %MM% %DD% %HH% %Min%"

call :DateToMinutes %stamp% NowMins

for /f "delims=" %%a in ('dir C:\Windows\System32\spool\PRINTERS\*.SPL /a-d /b') do call :CheckMins "%%a"

c:\batch\sleep 60
Goto :top

:CheckMins
::echo checkmins
set "filestamp="
set "filemins="
set "MinsOld="
set "YY=" & set "YYYY=" & set "MM=" & set "DD="
set "HH=" & set "Min=" & set "Sec=" & set "dt="
set "file=%~1"
set "filea=%file:\=\\%"
WMIC DATAFILE WHERE name="C:\\Windows\\System32\\spool\\PRINTERS\\%file%" get lastmodified | find "." >file.tmp
for /f %%a in (file.tmp) do set "dt=%%a"
set "YY=%dt:~2,2%" & set "YYYY=%dt:~0,4%" & set "MM=%dt:~4,2%" & set "DD=%dt:~6,2%"
set "HH=%dt:~8,2%" & set "Min=%dt:~10,2%" & set "Sec=%dt:~12,2%"
set "filestamp=%YYYY% %MM% %DD% %HH% %Min%"
::echo %filestamp%
del file.tmp 2>nul

if not defined yyyy goto :EOF

call :DateToMinutes %filestamp% FileMins

set /a MinsOld=%NowMins%-%FileMins%
::echo Now:%NowMins% File:%FileMins% Fileage:%minsold% "%~1"
if %MinsOld% gtr 20 call :DoThis
goto :EOF

:DoThis
::echo oldfile "%file%"
::c:\batch\sysinternals\handle %file%
for /f "tokens=1,2,3,4* delims=. " %%a in ("%file%") do set spoolno=%%a
set spoolno=%spoolno:0=%
cscript C:\Windows\System32\Printing_Admin_Scripts\en-US\prnjobs.vbs -l -j %spoolno% >c:\batch\spoolfile.txt
for /f "tokens=*" %%a in (emails.txt) do C:\batch\gbmail.exe -to %%a -h smtp.host.com -from sender@host.com -s "%file% has been in the system for %MinsOld% minutes on %COMPUTERNAME%" -file spoolfile.txt >nul
::After emailing - sleep for 5 minutes
c:\batch\sleep 300
goto :EOF

:DateToMinutes
setlocal
set yy=%1&set mm=%2&set dd=%3&set hh=%4&set nn=%5
if 1%yy% LSS 200 if 1%yy% LSS 170 (set yy=20%yy%) else (set yy=19%yy%)
set /a dd=100%dd%%%100,mm=100%mm%%%100
set /a z=14-mm,z/=12,y=yy+4800-z,m=mm+12*z-3,j=153*m+2
set /a j=j/5+dd+y*365+y/4-y/100+y/400-2472633
if 1%hh% LSS 20 set hh=0%hh%
if /i {%nn:~2,1%} EQU {p} if "%hh%" NEQ "12" set hh=1%hh%&set/a hh-=88
if /i {%nn:~2,1%} EQU {a} if "%hh%" EQU "12" set hh=00
if /i {%nn:~2,1%} GEQ {a} set nn=%nn:~0,2%
set /a hh=100%hh%%%100,nn=100%nn%%%100,j=j*1440+hh*60+nn
endlocal&set %6=%j%&goto :EOF

Open in new window

~bp

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
altquarkAuthor Commented:
Hi Bill

The main issue has definitely gone - but now I'm getting a "Maximum setlocal recursion level reached." message.  This happens every minute now.  This doesn't seem to stop the batch program from running - so I'm in a lot better position.  However, its still annoying - can you shed some light onto this new error ?

Jon
Bill PrewIT / Software Engineering ConsultantCommented:
Try moving line 7 above line 4.

~bp
altquarkAuthor Commented:
ok - Modified and testing....I'll let you know how it goes !
altquarkAuthor Commented:
It looks good after 7 hours.  So, I'm going to accept your script as a solution !  Thankyou !
altquarkAuthor Commented:
Move line 7 (setlocal) to line 4 (before the :top) and it works great !  Thankyou !
Bill PrewIT / Software Engineering ConsultantCommented:
Excellent, thanks for the feedback, glad that was helpful.

~bp
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Microsoft DOS

From novice to tech pro — start learning today.