Search code examples
variablesbatch-fileechoindirection

Batch: variable indirection: getting the value of a variable by dynamically constructed name


So when I run this code:

@echo off
setlocal enabledelayedexpansion

set lstFolders= First Second 

set intCounter=0
for %%i in (!lstFolders!) do (
    set /a intCounter += 1
    set strFlder=%%i
    set strFolder!intCounter!=!strFlder!
    echo %%i
    echo !strFlder!
    echo !strFolder%intCounter%!
    echo !strFolder1!
    echo !strFolder2!
)

:End
pause
endlocal

It results with this:

First
First
ECHO is off.
First
ECHO is off.
Second
Second
ECHO is off.
First
Second

Why doesn't it allow me to echo the variable create with the format : !strFolder%intCounter%!? Is there another way to reference this variable and get the data that is inside of it?


Solution

  • Caveat: The code below only works with list values (the tokens of %lstFolders% such as First) that:

    • contain neither spaces
    • nor any of the following chars.: & | < > "

    A different looping approach would be needed to handle such cases.

    @echo off
    setlocal enabledelayedexpansion
    
    set "lstFolders=First Second" 
    
    set intCounter=0
    for %%i in (%lstFolders%) do (
          rem Increment the counter
        set /a intCounter += 1
          rem Echo the loop variable
        echo #!intCounter!=%%i
          rem Set variable strFolder<intCounter> to the loop variable's value
        set "strFolder!intCounter!=%%i"
          rem Echo the variable created using variable indirection with for /f ('...')
        for /f "delims=" %%v in ('echo "%%strFolder!intCounter!%%"') do set "thisFolder=%%~v"
        echo %%thisFolder%% ^(via %%strFolder!intCounter!%%^)=!thisFolder!
    )
    

    Running the above yields:

    #1=First
    %thisFolder% (via %strFolder1%)=First
    #2=Second
    %thisFolder% (via %strFolder2%)=Second
    

    What you're looking for is variable indirection:

    • While you can set a variable indirectly (by a name that you construct dynamically from the value of another variable), e.g.,
      • set "strFolder!intCounter!=%%i", with !intCounter! having a value of 1, correctly sets variable strFolder1 to the value of %%i),
    • you cannot get a variable's value that way; you need an extra evaluation step, which for /f ... ('echo ...') can provide.:

      • for /f "delims=" %%v in ('echo "%%strFolder!intCounter!%%"') do ... parses the output of the command in single quotes (echo ...) and assigns the result as a whole (delims=) to variable %%v
        (%%~v removes the enclosing double quotes, which were added around the echo argument to make the command handle shell metacharacters such as & | < > correctly).

      • %%strFolder!intCounter!%% immediately evaluates strFolder!intCounter! to strFolder1, if !intCounter! is 1, which, thanks to the enclosing doubled % instances, ends up as literal %strFolder1%, which is what the echo command sees when it is run by the for command, causing it to evaluate the variable reference and expand to its value.