Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BAT-file: variable contents as part of another variable

Suppose i have variable a with contents "123" and variable b123 with some text in it. For some reason i want to use variable a as part of second var name. Something like this:

SET a=123
SET b123=some_text_in_it
rem i want next line to output  "some_text_in_it"
echo %b%a%%

so i want to concatenate text with var contents and use resulting string as variable name to echo that contents. Sample above doesn't work and i see why, probably i need to add some kind of groupping. How to do it, prefferably in one line?

like image 702
Aleksandr Kravets Avatar asked Mar 14 '12 10:03

Aleksandr Kravets


People also ask

What does %% A do in batch?

Use double percent signs ( %% ) to carry out the for command within a batch file. Variables are case sensitive, and they must be represented with an alphabetical value such as %a, %b, or %c. ( <set> ) Required. Specifies one or more files, directories, or text strings, or a range of values on which to run the command.

What is %% P in batch file?

The /P switch allows you to set the value of a variable to a line of input entered by the user. Displays the specified promptString before reading the line of input. The promptString can be empty.

What does %1 mean in a batch file?

When used in a command line, script, or batch file, %1 is used to represent a variable or matched string. For example, in a Microsoft batch file, %1 can print what is entered after the batch file name.

How do you change the content of a file using batch file?

Edit a batch file from within WindowsOnce you've clicked edit, your default text editor opens the file and allows it to be modified. You can also open any text editor or word processor and from within that program open the batch file. After making all changes save and exit the file.


3 Answers

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!
)
like image 137
jeb Avatar answered Oct 20 '22 04:10

jeb


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%]%%]!
like image 2
Aacini Avatar answered Oct 20 '22 03:10

Aacini


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!
like image 2
dbenham Avatar answered Oct 20 '22 03:10

dbenham