Wayfarer
Wayfarer

Reputation: 423

Variable not being correctly evaluated when EnableDelayedExpansion is set

I have a variable that apparently is not being correctly evaluated in a condition.

The scenario is the following: I've made a batch file that is set with SetLocal EnableExtensions, then it has a main routine that is set with SetLocal EnableExtensions EnableDelayedExpansion. The main routine calls a subroutine from within a nested if statement passing two parameters, and it gets a variable as a result. The subroutine being called is set with SetLocal EnableExtensions.

Problem is that when evaluating the variable that returns the subroutine, apparently the variable is not being correctly evaluated: a variable that one line before is echoed and confirmed to be equal to zero makes an if !variable! EQU 0 statement inexplicably return FALSE.

More explanations and details after the code below.

Sample code (Proof of Concept)

@echo off
SetLocal EnableExtensions 
rem Initialize variables
rem -------------------------------------------------------------------------------
set ErrorStatus=0
set PreConditionOne=1
set PreConditionTwo=1
set ValueOne=50
set ValueTwo=50

rem - Main Routine
rem -------------------------------------------------------------------------------
SetLocal EnableExtensions EnableDelayedExpansion 
rem Setting initial values for flag variables, value can be: 0=NO, 1=YES, 2=Not Checked.
set PreCheckFail=2
set ParamsNotEqual=2
rem Prechecks: Preconditions must be met in order to check variables.
echo Checking PreConditions. 
if %PreConditionOne%==1 (
    echo OK: First PreCondition is met.
    echo Checking Second PreCondition.
    if %PreConditionTwo%==1 (
        echo OK: Second PreCondition is met.
        echo Calling subroutine to check if values match.
        call :Subroutine %ValueOne% %ValueTwo%
    echo Back from subroutine.
        echo InLoop: ErrorLevel: %ErrorLevel%, ErrorStatus: %ErrorStatus%, ParamsNotEqual: %ParamsNotEqual%
        echo InLoopDelayed: ErrorLevel: !ErrorLevel!, ErrorStatus: !ErrorStatus!, ParamsNotEqual: !ParamsNotEqual!
    rem This condition fails to correctly check ParamsNotEqual, it works fine with ErrorLevel.
        if !ParamsNotEqual! EQU 0 (
            echo OK Parameteres verified successfully.
            set PreCheckFail=0
        ) else (
            echo ERROR Parameter verification failed.
            set PreCheckFail=1
        )
    ) else (
        echo ERROR: Second PreCondition is not met.
        set PreCheckFail=1
    )
) else (
    echo ERROR: First PreCondition is not met.
    set PreCheckFail=1
)
echo OutLoop: ErrorLevel: %ErrorLevel%, ErrorStatus: %ErrorStatus%, ParamsNotEqual: %ParamsNotEqual%
echo PreCheckFail: %PreCheckFail%
if %PreCheckFail% EQU 0 (echo "PreCheckFail IS ZERO") else (echo "PreCheckFail NOT ZERO")
endlocal

goto :Finish

:Subroutine
rem Subroutine
rem -------------------------------------------------------------------------------
SetLocal EnableExtensions
set ParamOne=%1
set ParamTwo=%2
echo This is the subroutine.
echo The first parameter passed is: %ParamOne%
echo The second parameter passed is: %ParamTwo%
if "%ParamOne%"=="%ParamTwo%" (
    echo OK^! Both variables are equal.
    set ParamsNotEqual=0
    set ErrorStatus=0
) else (
    echo ERROR^! Both variables are different.
    set ParamsNotEqual=1
    set ErrorStatus=1
)
echo EndSub: ErrorLevel: %ErrorLevel%, ErrorStatus: %ErrorStatus%, ParamsNotEqual: %ParamsNotEqual%
endlocal & set ParamsNotEqual=%ParamsNotEqual% & exit /b %ErrorStatus%

:Finish
endlocal

If you run this script, you will see that the subroutine correctly returns the ParamsNotEqual variable as 0, then the echo lines correctly show the values for both the normally parsed variables and the delayed ones:

InLoop: ErrorLevel: 0, ErrorStatus: 0, ParamsNotEqual: 2
InLoopDelayed: ErrorLevel: 0, ErrorStatus: 0, ParamsNotEqual: 0

But when the condition if !ParamsNotEqual! EQU 0 is evaluated, it just fails. Setting @echo on does not clarify anything, as the delayed variables are expanded on execution time so I cannot see what is really being evaluated there.

Test I've done so far

Things that have no effect so it keeps failing:

  1. Set SetLocal EnableExtensions EnableDelayedExpansion for the whole script
  2. Set SetLocal EnableExtensions EnableDelayedExpansion for the subroutine too.
  3. Not setting previously the ParamsNotEqualvariable.
  4. Evaluating the condition surrounding it with quotes, or brackets, or using == instead of EQU.
  5. Evaluating if !ParamsNotEqual! EQU 2 (which is the initial value of the variable) also returns FALSE

Possible workarounds:

  1. Using !ErrorLevel! instead of !ParamsNotEqual! works fine. This looks logic, as ErrorLevel is a system variable, not a user-defined one.

  2. Not setting SetLocal for the subroutine, hence having the subroutine variables not isolated from the rest of the script, makes the main routine automagically work fine as it is. This has no sense to me but makes me think that this error may be due to some kind of restriction either when calling a subroutine from within a nested if when EnableDelayedExpansion is set, or with using nested SetLocal statements.

I would like to keep using the value of ParamsNotEqual instead of ErrorLevel, and also, I would like to keep the variables within the subroutine isolated with SetLocal.

So, my question: Is this strange behaviour due to something am I doing wrong or is this a restriction in the way the command processor handles variables when EnableDelayedExpansion is set?

Any hint will be greatly appreciated. If you need any further tests or clarification on the above don't hesitate on asking. A lot of thanks in advance.

Upvotes: 4

Views: 187

Answers (1)

jeb
jeb

Reputation: 82247

The result is correct as ParamsNotEqual isn't equal to 0, as you set it to 0<space>.

This line contains the problem.

endlocal & set ParamsNotEqual=%ParamsNotEqual% & exit /b %ErrorStatus%

You should always use the extended syntax of SET with surrounding quotes

endlocal & set "ParamsNotEqual=%ParamsNotEqual%" & ...

Upvotes: 4

Related Questions