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.

5
  • Please note that superuser.com is not a free script/code writing service. If you tell us what you have tried so far (include the scripts/code you are already using) and where you are stuck then we can try to help with specific problems. You should also read How do I ask a good question?.
    – DavidPostill
    Jul 4, 2019 at 18:41
  • Sorry, I forgot to add the code. Now it is included.
    – Notanamber
    Jul 4, 2019 at 19:20
  • Two thoughts: First, this is unnecessarily hard to do in batch, but would be trivial in another language such as PowerShell. Second, you would be better off asking how to accomplish your end goal rather than asking for help implementing your proposed solution. See the XY Problem. Jul 4, 2019 at 22:00
  • (1) You may be making a lot of unnecessary work for yourself. You seem to be reading the entire file into memory, and then processing it. Are you going to be processing the data multiple times? Why not just process them as you read them?  (2) Why are you doing %_Lines%-1 (why are you subtracting one)?  (3) Rather than set /a count = 1 and set /a count += 1, you could just do set count=%%I. Jul 5, 2019 at 1:58
  • Thank you, I'll try to change my approach then and process the file as it's being read or I'll switch from batch to something else. I was not aware of its limitations as I'm inexperienced.
    – Notanamber
    Jul 5, 2019 at 4:18

1 Answer 1

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.