2

I am using the following loop in a batch file to individually call 'functions' in the file starting by displaying text and finishing with an errorlevel check and display.

Calling the loop function uses 2 variables..

  1. the sub function name to be called
  2. the text to echo on screen for user ease

This is what those calling lines look like

call :loop BackupDatabase "Taking database backup"
call :loop StopBGService "Stopping the background service"

This is the loop function itself

:loop
    :retry
    echo %~2
    call :%~1

    IF %ERRORLEVEL% NEQ 0 (
        SET ERRORLEV=%ERRORLEVEL%
        ECHO Executing %~2 failed
        ECHO Error Level: %ERRORLEV%
        ECHO.

        set /p ans=Do you want to retry %~2 [y/n]^>
        if "%ans%"== "y" goto :retry
        if "%ans%"== "Y" goto :retry
    )
GOTO :EOF

Below you would find the functions that are called by each loop

:BackupDatabase
    Perform database backup commands
GOTO :EOF

:StopBGService
    Perform stop background service commands
GOTO :EOF

The problem I am having is when a "loop" goes wrong the script should ask if a retry is required, the very first time this happens, if you enter "n" it still retries? I've read over the loop script over and over and can't tell why this is. If anyone can fix this it would be a great help, or if someone can point out a better way of doing what I am trying to achieve.

1
  • Did you try using choice instead of set?
    – Karan
    Jul 3, 2013 at 17:23

1 Answer 1

2

The problem is that the Windows Command Prompt / Command-Line Interpreter (CLI), known as CMD.EXE, has a bug (well, I’m sure Microsoft thinks that it’s a feature) wherein, when it reads a block construct like IF … ( … ) or a FOR loop, it interprets all %variable_name% variables immediately, before the loop is executed.  In your example, if ans is null when you enter the IF %ERRORLEVEL% NEQ 0 ( … ) block, then all occurrences of %ans% inside that block evaluate to null, even if you change ans inside the block.  You can see this if you leave ECHO on (or turn it back on just before IF %ERRORLEVEL% NEQ 0).

The fix is to tell CMD to allow variables to be expanded at the right time, when the statements referring to them are executed.  You do this by adding

setlocal enabledelayedexpansion

somewhere near the beginning of your batch file, and changing your user-dialog code to look like

    set /p ans=Do you want to retry %~2 [y/n]^>
    if "!ans!" == "y" goto :retry
    if "!ans!" == "Y" goto :retry

using the !variable_name! form to activate the delayed expansion.  See SET /? and SETLOCAL /? for more information.

5
  • That's perfect, thanks Scott. Could you see any reason why not to include setlocal enabledelayedexpansion by default to all my batch scripts, and then use the !variable_name! notation when required? Jul 4, 2013 at 9:48
  • Hmm. I vaguely recall having a batch file once that needed delayedexpansion turned off, but it was doing something unusual. It’s probably a pretty good idea to turn it on all the time. Jul 11, 2013 at 16:25
  • It doesn't seem to be causing me any problems, so I'll leave it off in this script or any others that require it to correctly use the FOR loop. If I were to have a script where it would cause an issue I would switch it off before the FOR loop and back on again after the FOR loop. Aug 2, 2013 at 13:21
  • @GhandiManning: Be careful of that. Did you look at SETLOCAL /?? You “switch off” a SETLOCAL by saying ENDLOCAL –– and that erases any variables that you set since the SETLOCAL, puts you back in the directory you were in before the SETLOCAL, and maybe some other things. Aug 2, 2013 at 19:02
  • Thanks, I will bare that in mind. As it stands the script doesn't require ENDLOCAL so I won't encounter those problems. Aug 8, 2013 at 11:11

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .