Use a variable in a 'for' loop - batch-file

I have following code:
#echo off
SET ITER=0
for %%i in (%*) do (
SET ITER+=1
ECHO %ITER%
)
The output is (for three arguments):
0
0
0
Expected output:
1
2
3
Why can't I access the updated variable in the for loop?

Expansion of variables with percents is done before a statement/block is executed.
So in your case the complete block is expanded before the echo %ITER% is executed, to constant echo 0.
The variable ITER itself is updated in the loop properly.
To avoid this, you could use the delayed expansion, this works like percent expansion but just in the moment of execution
#echo off
setlocal EnableDelayedExpansion
SET ITER=0
for %%i in (%*) do (
SET /a ITER+=1
ECHO !ITER!
)

Related

Strange behavior inside IF block in a batch file

I have this batch file here and it doesn't run well. It says "( was unexpected at this time". Do you know why?
#ECHO OFF
SET A[0]=HOLA
SET A[1]=HELLO
SET I=0
:LOOP
IF DEFINED A[%I%] (
ECHO %I%
CALL SET B=%%A[%I%]%%
CALL ECHO %%B%%
IF %B% == HOLA (
ECHO ES
) ELSE (
ECHO EN
)
PAUSE
SET /A I+=1
GOTO :LOOP )
My expected output should be:
0
HOLA
ES
1
HELLO
EN
I don't understand why inside IF everything works different. Thanks in advance.
#ECHO OFF
SETLOCAL
SET "A[0]=HOLA"
SET "A[1]=HELLO"
SET /a I=0
:LOOP
IF DEFINED A[%I%] (
ECHO %I%
CALL SET "B=%%A[%I%]%%"
CALL ECHO %%B%%
FOR /f "delims==" %%b IN ('set B') DO IF /i "%%b"=="B" (IF "%%c"=="HOLA" (SET "Same=Y") ELSE SET "Same=")
IF DEFINED Same (
ECHO ES
) ELSE (
ECHO EN
)
rem PAUSE
SET /A I+=1
GOTO LOOP
)
GOTO :EOF
Use set "var=value" for setting string values - this avoids problems caused by trailing spaces. Don't assign a terminal \, Space or " - build pathnames from the elements - counterintuitively, it is likely to make the process easier. If the syntax set var="value" is used, then the quotes become part of the value assigned.
Your set syntax sets the value of the variable to the rest of the line.
Hence, the line including the label loop would be assigned to i.
if %B%... substitutes the value of B at the time the block was parsed. AT that time, B was not defined, so nothing is substituted, so the result is if == HOLA (... and cmd does not expect ( at this point and objects.
The complex line I've shown performs a set command on all variables that start b and assigns %%b to the variable name, %%c to its current value. There may be many variables that start B, so we need to select exactly B, and if B's value in %%c is HOLA then set Same, otherwise set Same to nothing, which means that Same will either be assigned, or not assigned respectively.
if defined operates on the current value of the variable, so same can be used safely to do the if processing.
BUT The clean way to do this is by using delayedexpansion.
Stephan's DELAYEDEXPANSION link

Why is an environment variable not updated in a loop?

I would like my variable res to be updated inside the loop, but it does not update until after the second iteration.
Here is my code:
#echo off
set /a res=10
:loop
if %res% gtr 100 (
goto endLoop
) else (
set /a res=res*10
rem Here the 'res' shouldn't it be 100?
echo the result is : %res%
rem Here the first iteration display 10 not 100.
goto loop
)
:endLoop
pause > nul
Is there an explanation for this?
As an example of usage of delayed expansion here's your modified code:
#Echo Off
SetLocal EnableDelayedExpansion
Set "res=10"
:loop
If %res% Lss 100 (
Set/A res*=10
Echo the result is : !res!
GoTo loop
)
Pause>Nul
There is an alternative without using delayed expansion in this case, but I'd suggest you stick with the former until you are confident enough to understand the order in which things are read and parsed:
#Echo Off
Set "res=10"
:loop
If %res% Lss 100 (
Set/A res*=10
Call Echo the result is : %%res%%
GoTo loop
)
Pause>Nul

an error with set /a in a loop

I have this code:
setlocal enableDelayedExpansion
set count=0
set letter=a,b,c
for %%a in (%letter%) do (
set /a "count+=1"
echo %count%
)
pause
The output is:
0
0
0
I want that the output will be:
1
2
3
I also tried to do it without EnableDelayedExpansion, but I had no luck. What did I do wrong?
you need to
echo !count!
with delayedexpansion
or
call echo %%count%%
%count% will always return the value of count as it stood when the block (parenthesised series of statements) was encountered.

variable as tokens in for loop

Im trying to make a batch file that loops thru an array containing numbers like this: 1 2 3 4 5.
In the first itteration of the loop I like to pick token 1 and 2. In the second 2 and 3, in the third 3 and 4 and so on.
I do think I should use ! in the variables first and second that I use as tokens. Like in the first FOR /F, but when I do, I get: !first!" was not expected here.
And if I use %, it does not count up.
Everything works except the variable tokens. Any one knowes how to? Any help or suggestions greatly appriciated.
This is the part Im struggeling with:
setlocal EnableDelayedExpansion
set first=1
set second=2
set N=4
set output="1 2 3 4 5"
set output=%output:"=%
for /L %%a in (1,1,%N%) do (
if !counter! equ active (
set /a first+=1
set /a second+=1
)
FOR /F "tokens=!first!" %%a IN ("%output%") DO (
set nr1=%%a
)
FOR /F "tokens=%second%" %%a IN ("%output%") DO (
set nr2=%%a
)
echo nr1 var: !nr1!
echo nr2 var: !nr2!
echo counter f: !first!
echo counter s: !second!
set counter=active
)
You cannot use delayed expanded variables in the options string of for /F. Neither can you use other for variables for that. But you can use normally (immediately) expanded variables. Also you can use argument references like %1, for example.
So a nice work-around for your problem is to place the for /F loop in a sub-routine and use call in the main program with the delayed expanded variables as arguments, like this:
#echo off
setlocal EnableDelayedExpansion
set /A first=1
set /A second=2
set /A N=4
set "output=1 2 3 4 5"
set "counter="
for /L %%a in (1,1,%N%) do (
if defined counter (
set /A first+=1
set /A second+=1
)
call :SUB !first! !second!
echo nr1 var: !nr1!
echo nr2 var: !nr2!
echo counter f: !first!
echo counter s: !second!
set "counter=active"
)
endlocal
exit /B
:SUB val_token1 val_token2
for /F "tokens=%~1,%~2" %%a in ("%output%") do (
if %~1 LSS %~2 (
set "nr1=%%a"
set "nr2=%%b"
) else if %~1 GTR %~2 (
set "nr1=%%b"
set "nr2=%%a"
) else (
set "nr1=%%a"
set "nr2=%%a"
)
)
exit /B
Since you are extracting tokens from the same string, I combined your two for /F loops into a single one. The if block in the for /F loop in the sub-routine :SUB is there just in case the second token number is not always greater than the first one. But if that can guaranteed, the for /F loop needs to contain only set "nr1=%%a" and set "nr2=%%b".

Squaring step variable - Batch for loop

Is it possible to square the increment/step variable in a batch for loop.
This is what it looks like right now
FOR /L %%A IN (1024,1000, 1048576) DO (
do stuff
)
however instead of going up by 1000 each time, I want to go up by 2^10, 2^11 .... 2^20 (1048576) is it possible to do that?
No, for /l loops can not handle geometric increments. But you can use batch arithmetics for it
for /l %%a in (10 1 20) do (
set /a "A=1<<%%a"
setlocal enabledelayedexpansion
for %%A in (!A!) do ( endlocal
rem Your code here - do stuff
echo %%A
)
)
Delayed expansion is needed to handle the changing variable inside the block of code. If your inner code has no problems with delayed expansion being active, it can be simplified as
setlocal enabledelayedexpansion
for /l %%a in (10 1 20) do (
set /a "A=1<<%%a"
rem Your code here - do stuff
echo !A!
)
If switching to PowerShell is fine for you, you can use
foreach ($i in 1..3) {
# example output
echo "1000^$i = $([Math]::Pow(1024, $i))"
# start "myprogram.bat --parameter $x
& 'myprogram.bat' #("--parameter", [Math]::Pow(1024, $i))
}

Resources