Suppose i have variable a with contents \"123\" and variable b123 with some text in it. For some reason i want to use vari
There are two common ways for this
CALL
or DelayedExpansion
setlocal EnableDelayedExpansion
SET a=123
SET b123=some_text_in_it
rem i want next line to output "some_text_in_it"
call echo %%b%a%%%
echo !b%a%!
The CALL
variant uses the fact, that a call will reparse the line a second time, first time only the %a%
will be expanded and the double %%
will be reduced to a single %
call echo %b123%
And in the second step the %b123%
will be expanded.
But the CALL
technic is slow and not very safe, so the DelayedExpansion should be prefered.
The DelayedExpansion works as the exclamation marks are expanded in a later parser phase than the expansion of the percents.
And that's also the cause, why delayed expansion is much safer.
Edit: Method for arrays containing numbers
If you are operating with arrays which contains only numbers you could also use set /a
to access them.
That's much easier than the FOR
or CALL
technic and it works also in blocks.
setlocal EnableDelayedExpansion
set arr[1]=17
set arr[2]=35
set arr[3]=77
(
set idx=2
set /a val=arr[!idx!]
echo !var!
)
There is yet one more way to accomplish the task that is very useful if both variables have been assigned within the same parenthesized block that is expanding them. The solution is similar to jeb's delayed expansion answer, except it uses a FOR variable instead of normal expansion.
@echo off
setlocal enableDelayedExpansion
:: Define a linefeed value - only needed for last variant
set LF=^
:: The two blank lines above are critical to the definition of LF
(
set A=123
set B123=some_text_in_it
rem ECHO !B%A%! will fail because A was assigned within the same block
rem This works as long as A does not contain * or ?
for %%A in ("!A!") do echo !B%%~A!
rem This works as long as A does not start with EOL character (; by default)
for /f "delims=" %%A in ("!A!") do echo !B%%A!
rem This will never fail because it disables EOL by setting it to a linefeed
for /f eol^=^%LF%%LF%^ delims^= %%A in ("!A!") do echo !B%%A!
)
The CALL trick could also be used, but comparatively it is very slow, and it is unreliable - see CALL me, or better avoid call.
The definition and use of a linefeed variable is discussed at Explain how dos-batch newline variable hack works.
Setting EOL to a linefeed is described in detail at HOW TO: FOR /F Disabling EOL or using a quote as delim. Using an LF variable instead of embedding linefeeds within the FOR statement is just an extension of the technique. It makes the code more readable.
EDIT
A simpler way to safely use the FOR technique for any valid value is to set EOL to an equal sign, since no user defined variable can contain an = in the name.
for /f "eol== delims=" %%A in ("!A!") do echo !B%%A!
The best reason to "use a variable as part of second var name" is to access array (vector and matrix) elements via a subscript variable. Although this is exactly the same than the example above, it looks much clearer:
setlocal EnableDelayedExpansion
set i=123
set v[123]=Value of element 123
call echo %%v[%i%]%%
echo !v[%i%]!
Although CALL
is slower than Delayed Expansion
, as jeb said above, it may be nested several times allowing to use a second (or deeper) indirection to access array elements. This feature may be used in complex data structures, like linked lists. For example:
set i=123
set v[123]=456
set w[456]=Value of element 456
call call echo %%%%w[%%v[%i%]%%]%%%%
EDIT: For some reason I don't understand, the Delayed Expansion version of the last command doesn't work properly:
call echo !w[%%v[%i%]%%]!