Search code examples
loopsbatch-filevariablescmd

Batch - Passing variables into new batch files inside for loop /L


I have a script that creates new batch files inside a for loop.

This is the code:

@echo on

SET "files=2"
SET "desired_count=2"

FOR /L %%G IN (1,1,%files%) DO (
    IF %%G LSS 10 (
        (
        echo echo on
        echo set "counter=0"
        echo set desired_count=%desired_count%
        echo.
        echo :startpoint
        echo set /a "counter+=1"
        echo IF "!counter!" LSS %desired_count% goto startpoint
        ) >> file_0%%G.bat
        
    ) 
)

I am completely lost when trying to pass variables inside for and if statements. The solution presented in some other forum posts suggests using set local EnableDelayedExpansion, but I have not managed to get that to work for me so far.

In the created .bat files I would like the script to loop until the desired_count is met.

The file_01.bat that is created is below.

echo on
set "counter=0"
set desired_count=2

:startpoint
set /a "counter+=1"
IF "!counter!" LSS 2 goto startpoint

When that is submitted it infinitely loops around this line:

IF "!counter!" LSS 2 goto startpoint

Playing around with different combinations of set local EnableDelayedExpansion, changed what happened, but I couldn't get it correctly count in the loop.


Solution

  • @echo on
    
    SETLOCAL
    
    SET "files=2"
    SET "desired_count=2"
    
    FOR /L %%G IN (1,1,%files%) DO (
        IF %%G LSS 10 (
            (
            echo echo on
    
            ECHO SETLOCAL ENABLEDELAYEDEXPANSION
    
            echo set "counter=0"
            echo set desired_count=%desired_count%
            echo.
            echo :startpoint
            echo set /a "counter+=1"
            echo IF !counter! LSS %desired_count% goto startpoint
            ) >> file_0%%G.bat
            
        ) 
    )
    

    [untested]

    Changes : Inserted setlocal after @echo on. This establishes a local environment which means that any changes made to the environment variables while running this code are discarded when the batch ends so the environment remains clean - not altered by successively running batch files.

    Inserted ECHO SETLOCAL ENABLEDELAYEDEXPANSION to follow the echo on in the generated file. The full command is SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION, but by default, ENABLEEXTENSIONS is enabled and ENABLEDELAYEDEXPANSION disabled, so this shorter form is usually adequate. This instruction causes cmd to interpret !var! as the run-time value of var (ie. as it might change within a loop.)

    Removed quotes from IF "!counter!" LS… As your original code would be interpreted, the statement would be resolved to "if "!counter!" LSS 2 …" where "!counter!" is precisely that - the literal string "!counter!". Since "!counter!" is less than 2, you get an infinite loop.

    The setlocal enabledelayedexpansion would change this interpretation to "if "thecurrentvalueofcounter" LSS 2 …" - but this is still incorrect as if syntax compares the expressions on both sides of the comparator (LSS) literally, so you might get "if "2" LSS 2 …" for instance. The string "2" is always less than 2. To accomplish what you need to do, you need to compare the two values, ie !counter! LSS 2.

    Note that IF "!counter!" LSS "2" g… may appear to work, but in this case, the comparison is performed as strings, hence "2" LSS "2","3" LSS "2", etc. Perfect... except "12" LSS "2" is true because the comparison is performed character-by-character and 1 is less than 2.

    However ------

    In the generated batch, you don't need to use the delayedexpansion syntax as counter is not contained in a code block (parenthesised sequence of commands)

    Hence, you could use

        echo IF %%counter%% LSS %desired_count% goto startpoint
    

    which would generate

    IF %counter% LSS 2 goto startpoint
    
    • as % "escapes" (turns off) the special meaning of %, and since the generated IF %counter%… code is not in a code block, there is no need for the delayedexpansion syntax here. This means that the delayedexpansion keyword is also not required.

    See Stephan's DELAYEDEXPANSION link for more info