0

I read through and tried solutions from this thread: Batch file. Variable in variable but it's beyond my understanding or it just won't work in my case.

I've got an .ini file that looks like this:

rep1=VAL
url1=VAL
rep2=VAL
url2=VAL
  ︙

In my batch file, I'm saving these values to variables: rep1 to %rep1%, url1 to %url1% and so on. Number of items in this .ini file is supposed to change, depending on needs, so I need to read the entire file.

Then, I want to execute a command in the "for" loop, for every set of two items, which looks like this:

program.exe -param1 %rep1% -param2 %url1%

So, in next loop iteration, it would be:

program.exe -param1 %rep2% -param2 %url2%

And so on, until I run out of .ini file. The problem is, I cannot "insert" a counter to the variable in the command. I've tried replacing number by counter, by doing something like this:

%rep%%counter%

but it won't call the %repX%, where "X" is the number, variable as intended.

Is there any way to do that in batch file?

My current code:

Set _File=list.ini
Set /a _Lines=0
For /f %%j in ('Find "" /v /c ^< %_File%') Do Set /a _Lines=%%j
::counting how many times for loop to go
set /a iterate=(%_Lines%-1)/2
::read the ini file
for /f "delims=" %%a in (list.ini) do set %%a

setlocal enableextensions enabledelayedexpansion
set /a count = 1
FOR /L %%I IN (1,1,%iterate%) DO (
    set "rep=%rep%%count%"
    set "url=%url%%count%"
    program.exe -param1 %rep% -installIU %url%
    set /a count += 1
)

%rep% and %url% just seem to be empty when I put echo before the command, whilst I try to make them %rep1% and %url1% on the first iteration, incrementing the number every time the loop goes.

1 Answers1

1

As Twisty Impersonator says, you may be dealing with an X-Y Problem.

X

Given an input file that looks like

rep1=The
url1=quick
rep2=brown
url2=fox
  ︙

you want to run commands like

program.exe -param1 The   -installIU quick
program.exe -param1 brown -installIU fox
    ︙

I would do that like this:

setlocal enabledelayedexpansion
set phase=1
for /f "delims== tokens=1*" %%A in (list.ini) do (
    if !phase! == 1 (
        set "saverep=%%B"
        set phase=2
    ) else (
        program.exe -param1 "!saverep!" -installIU "%%B"
        set phase=1
    )
)

The for statement reads everything before the first = into %%A and everything after it into %%B.  (Either value may contain space(s).  We do not expect %%A to ever contain spaces because it will be repN or urlN.  The %%B value may contain additional = signs.)  The phase variable alternates between 1 and 2.  It is 1 when we read a repN and 2 when we read a urlN.

When we read a repN (when phase is 1), save the value in saverep (and set phase to 2).  When we read a urlN (when phase is 2), run the program using the saved rep and the URL value from the current line.  I added quotes ("!saverep!" and "%%B") to handle values with spaces in them.

This totally ignores the variable names in the file (i.e., the repN and urlN strings that appear to the left of the =), even though we have access to them (in %%A).  If you are concerned about possible data integrity issues (skipped entries, invalid entries, multi-line entries, etc.) in your file, you can certainly add code to do sanity checks on %%A.

Y

Given an input file that looks like

rep1=The
url1=quick
rep2=brown
url2=fox
  ︙

you want to read the file, set variables rep1=The, url1=quick, rep2=brown, url2=fox, etc… in the environment, and then loop through those environment variables and run commands like

program.exe -param1 The   -installIU quick
program.exe -param1 brown -installIU fox
    ︙

Here’s a way to do that:

Set /a _Lines=0
For /f %%j in ('Find "" /v /c ^< %_File%') Do Set /a _Lines=%%j
::counting how many times for loop to go
set /a iterate=(%_Lines%-0)/2
::read the ini file
for /f "delims=" %%a in (list.ini) do set %%a

setlocal enableextensions enabledelayedexpansion
FOR /L %%I IN (1,1,%iterate%) DO (
    set count=%%I
    call set "rep=%%rep!count!%%"
    call set "url=%%url!count!%%"
    program.exe -param1 "!rep!" -installIU "!url!"
)

This is obviously fairly close to what you have now.  Notes:

  • You probably don’t need to do Set /a _Lines=0 (i.e., you could probably just leave out that line).
  • You might want to verify that _Lines has a valid value after the For … ('Find …) … command.  To be really paranoid, you could initialize _Lines to a totally invalid value (like none or null).  If _Lines is still the initial value after the For … ('Find …) … command, that means that it failed.  If _Lines is anything other than an even, positive integer, then there’s something wrong.
    • If _Lines is 0 (zero) or an odd, positive integer, then there’s something wrong with your file.
    • If _Lines is anything else (a negative number, a non-integer like 3.14, or a non-number like foo), then there’s something wrong with the Find command.
  • I set iterate to (%_Lines%-0)/2 to make it obvious that I had made a change.  I believe that you should be able to just set /a iterate=%_Lines%/2.  After all, if the file is 42 lines long, then you have 21 data points (i.e., 21 pairs of values, needing 21 command executions), right?
    If you have a good reason for subtracting one, then put the -1 back in (but you might want to document the reason, if only as a comment in your code).
  • I left enableextensions in there, because you had it, but, as far as I know, you don’t need it.
  • Since we’re doing a FOR /L loop, we don’t also need to do arithmetic on a count variable; just set it equal to the loop index.
  • Finally, the payoff:

    call set "rep=%%rep!count!%%"
    call set "url=%%url!count!%%"
    

    If count is (for example) 17, then the first command will build a string that looks like "rep=%rep17%".  We then “call” the set "rep=%rep17%" command, which re-parses / re-interprets / evaluates that command and sets rep equal to the value of %rep17%.  (Obviously the second line does the exact same thing for url.)

    I got the idea for this way of using the call statement from Squashman’s answer to batch increment variable string.  Variations are discussed in other threads; e.g., Batch file. Variable in variable (although not all the answers to that question work in loops).


Y2

The second half of the above might be clearer if I present it like this:

setlocal enabledelayedexpansion
FOR /L %%I IN (1,1,%iterate%) DO (
    call :kludge rep%%I url%%I
    program.exe -param1 "!rep!" -installIU "!url!"
)
goto :eof

:kludge
set "rep=!%1!"
set "url=!%2!"
exit/b

Here we are building strings like rep17 and url17 and passing them to the :kludge subroutine, where they are re-parsed in the context

set "rep=!rep17!"
set "url=!url17!"

Note that this solution eliminates the count variable.


Yikes!

Here’s another variation that’s even shorter, although perhaps less clear:

setlocal enabledelayedexpansion
FOR /L %%I IN (1,1,%iterate%) DO (
    call set "rep=%%rep%%I%%"
    call set "url=%%url%%I%%"
    program.exe -param1 "!rep!" -installIU "!url!"
)

The cryptic expression %%rep%%I%% is parsed like this:

%%rep%%I%%
▲▲   ↑-↑▲▲
▲▲______▲▲
  • First the %%I is recognized as the loop index variable and is replaced with its value (e.g., 17), resulting in a string like %%rep17%%.
  • The remaining %% pairs are reduced to individual % characters: %rep17%.
  • The %rep17% expression is interpreted as the 17th rep variable.

Note that this solution also avoids using a count variable.