Using the Windows XP CMD command-line I can expand a variable twice as follows:
set AAA=BBB
set BBB=CCC
for /F "usebackq tokens=*" %i in (`echo %%AAA%%`) do echo %i
will echo CCC
. I.e. AAA
has been expanded to the string BBB
, and then the variable BBB
has been expanded to CCC
.
This doesn't work from inside a batch script (i.e. a .cmd file). Changing the %%AAA%%
to either %%%AAA%%%
or %%%%AAA%%%%
doesn't work either.
Any idea how i can achieve this from within a script, namely to take expand the variable AAA to the string CCC?
Late Edit
The answers posted work for my reduced example however the non-tortuous answer doesn't work for the real case. Here's an extended example (which doesn't work), which illustrates what I was actually trying to do:
setlocal enabledelayedexpansion
set LIST=BBB CCC DDD
set BBB=111
set CCC=222
set DDD=333
for %%i in (%LIST%) do (
for /F %%a in ('echo %%%i%') do echo !%%a!
)
I would like to see
111
222
333
output.
Thinking in terms of a less tortuous solution, this, too, produces the CCC
you desire.
setlocal enabledelayedexpansion
set AAA=BBB
set BBB=CCC
for /F %%a in ('echo %AAA%') do echo !%%a!
edit:
to dissect this answer:
setlocal enabledelayedexpansion
- this will allow for any environment variable setting during your bat to be used as modified during the process of your for
loop.
set AAA=BBB
, set BBB=CCC
- your data population set
statements
for /F %%a in ('echo %AAA%') do echo !%%a!
- This tells the processor to loop, albeit only once, and take out the first token that is returned (default delimiter of space and tab apply) from the running of the command in the parens and put it in the var %%a (outside of a batch, a single % will do). If you specify that var as %%a, you need to use %%a in your do
block. Likewise, if you specify %%i, use %%i in your do
block. Note that to get your environment variable to be resolved within the do
block of the for
loop, you need surround it in !
's. (you don't need to in the in
block, as I originally posted - I have made that change in my edit).
edit:
You were very close with your updated example. Try it like this:
@echo off
setlocal enabledelayedexpansion
set LIST=BBB CCC DDD
set BBB=111
set CCC=222
set DDD=333
for %%i in (%LIST%) do (
for /F %%a in ('echo %%i') do echo !%%a!
)
The difference between your update and this is that you were trying to echo the environment variable in the in
set with in ('echo %%%i%')
, but without the !
's for the delayed expansion of set variables. Were you to use in ('echo !%%i!')
, you would see your BBB
, CCC
, and DDD
variables resolved, but then the do
block of your inner loop wouldnt have anything to resolve - you dont have any 111
environment variables. With that in mind, you could simplify your loop with the following:
@echo off
setlocal enabledelayedexpansion
set LIST=BBB CCC DDD
set BBB=111
set CCC=222
set DDD=333
for %%i in (%LIST%) do (echo !%%i!)
How to expand a shell variable multiple times:
@echo off
setlocal enabledelayedexpansion
set myvar=second
set second=third
set third=fourth
set fourth=fifth
echo Variable value before expansion: !myvar!
call :expand myvar
echo Variable value after expansion: !myvar!
goto :eof
:expand
set var=%1
:expand_loop
if not "!%var%!" == "" (
set var=!%var%!
goto :expand_loop
)
set %1=!var!
goto :eof
output:
Variable value before expansion: second
Variable value after expansion: fifth
The following (torturous) approach seems to work okay:
@echo off
:main
setlocal enableextensions enabledelayedexpansion
set aaa=bbb
set bbb=ccc
call :myset x %%aaa%%
echo %x%
endlocal
goto :eof
:myset
for /F "usebackq tokens=*" %%i in (`echo %%%2%%`) do set %1=%%i
goto :eof
It outputs:
ccc
as desired.
I've often used that trick to (for example) format %aaa%
into %x%
to a certain size (a la sprintf
) but this is the first time I've had to do double indirection. It works because you don't find the extra "%%"
being sucked up by the current shell level.
This aaa.bat
@echo off
set aaa=bbb
set bbb=ccc
for /F %%i in ('echo %%%aaa%%%') do echo %%i
outputs
c:>ccc
What exactly is the trouble?
This is a somewhat dated question, but, interestingly, the answer is simpler now. I'm running these scripts on Windows 10:
setlocal enabledelayedexpansion
set AAA=BBB
set BBB=CCC
echo !%AAA%!
outputs
CCC
For the extended version, this works:
setlocal enabledelayedexpansion
set LIST=BBB CCC DDD
set BBB=111
set CCC=222
set DDD=333
for %%i in (%LIST%) do (
echo !%%i!
)
outputs
111
222
333
Double indirection (which is a very apt way pax
has put it) reminds of C pointer de-referencing.
I doubt that is supported in the Windows command shell.
Do you think the larger purpose you are targeting can be achieved with something easier;
maybe lesser of a code-golf approach then the one contrived nicely by pax
?
it seems like the easiest thing to do is to create a tiny temp file to set the variable as you wish. something like this should work:
setlocal enabledelayedexpansion
set LIST=BBB CCC DDD
set BBB=111
set CCC=222
set DDD=333
for %%i in (%LIST%) do (
echo set a=\%%%i\%>tmp.bat
call tmp.bat
echo %a
rm tmp.bat
)