Why won't this batch code work? - batch-file

Assuming that %Edata% was a variable written like this "A;B;C;1;2;3;" then this code should be able to separate it into a bunch of numbered variables:
set /a c=0
FOR %%A IN (%Edata%) DO (
set /a c=%c%+1
set var%c%=%%A
echo.^>^>^> Set "%%A" to "var%c%"
)
Only the result is setting all parts of the variable to var0 because the %c% variable doesn't count up each time like it's supposed to. Could someone explain why?

This code works:
#echo off
setlocal EnableDelayedExpansion
set /a c=0
FOR %%A IN (%Edata%) DO (
set /a c=!c!+1
set var!c!=%%A
echo.^>^>^> Set "%%A" to "var!c!"
)
The problem in your script is that variables by default are expanded at parse time and not at execution time. In this case c is expanded only once, before entering the loop, that's why its value is always 0 and it never changes.
You have to enable the expansion of variables at execution time with this command:
setlocal EnableDelayedExpansion
and you have to use !c! instead of %c% inside the for loop.

Related

Batch - Variable substring in a For loop, with variable range

For the record, seeing that this type of questions is pretty popular on SO, I read around 30 answers in SO and couldn't find a question and answer that fit perfectly to my needs.
I have the following snippet:
#echo off
setlocal EnableDelayedExpansion
set string=test
for /l %%l in (0,1,1) do (
set /A remainder=%%l %%2
if remainder equ 1 (
set /A curr=%%l+1
call set res=!string:~-%curr%,1!
echo !res!
)
)
When executed this way, I get:
string:~-curr
How do I get the line with the variable substring with variable range - call set res=!string:~-%curr%,1!, to execute properly? For instance, for the second index (1), I want it to execute as if set res=!string:~-2,1! is written there.
How do I solve this?
You cannot use normal (immediate) expansion for the variable curr since you write and read it in the same block of code. However, since nested delayed expansion (like !string:~-!curr!,1!) does not work (because !string:~-! and !,1! were seen as variables then, and the former was even invalid syntax), you could use an interim for loop:
#echo off
setlocal EnableDelayedExpansion
set "string=test"
for /L %%l in (0,1,1) do (
set /A "remainder=%%l%%2"
if !remainder! equ 1 (
set /A "curr=%%l+1"
for %%k in (!curr!) do (
set "res=!string:~-%%k,1!"
echo(!res!
)
)
)
endlocal
(I prefer this over the approach call set "res=%%string:~-!curr!,1%%", because call may introduce trouble with the caret symbol ^ and it is slower.)
I have no idea, what your code is supposed to do, but to clear the error you mention:
#echo off
setlocal EnableDelayedExpansion
set string=test
for /l %%l in (0,1,1) do (
set /A remainder=%%l %%2
if !remainder! equ 1 (
set /A curr=%%l+1
call set res=%%string:~-!curr!,1%%
echo !res!
)
)

"for" loop not working as per need

I have following code:
set b=Hello
set c=1
set d=5
for /l %%x in (1,1,%d%) do (set /a c=c+1 & set "a=%a%%b%%c%")
echo %a%
And for this, I wanted an output as:
Hello1Hello2Hello3Hello4Hello5
But instead, I get result as:
Hello1Hello1Hello1Hello1Hello1
Can anyone give me any idea on how can I do so?
You need:
setlocal enableextensions enabledelayedexpansion
set b=Hello
set c=0
set d=5
for /l %%x in (1,1,%d%) do (set /a c=c+1 & set "a=!a!!b!!c!")
echo %a%
endlocal
The ! variant of % will expand the variables at the time the code is executed whereas % expands when the code is parsed. And, since parsing happens on the entire statement (from for to the closing parenthesis), you'll get the original value only.
Note that you don't need delayed expansion for the invariant b but, once you've decided you need them, you may as well use them everywhere - they act more as you'd expect in most cases.
You'll notice I've also changed the initial value of c to get the output you stated that you wanted. As it was, you would get 2,3,4,5,6 rather than 1,2,3,4,5.
I think this is what you want:
#Echo Off
Set "a="
Set "b=Hello"
Set "c=1"
Set "d=5"
For /L %%A In (%c%,1,%d%) Do Call Set "a=%%a%%%b%%%A"
If Defined a Echo %a%
Pause
Or alternatively:
#Echo Off
Set "a="
Set "b=Hello"
Set "c=1"
Set "d=5"
For /L %%A In (1,%c%,%d%) Do Call Set "a=%%a%%%b%%%A"
If Defined a Echo %a%
Pause

Batch-File Variable Part of Another Variable

Okay so let's say I have the following code
set playerlevel=5
set x=player
echo %x%level
I want the output to be "5", is that possible?
dbmitch's answer is good for integers, but if you want to display strings or integers, you can simply use delayed expansion.
#echo off
setlocal enabledelayedexpansion
set playerlevel=5
set x=player
echo !%x%level!
Note that if the code is located inside of a code block (i.e. enclosed in parentheses), the syntax is slightly different.
#echo off
setlocal enabledelayedexpansion
REM This is just an example code block to show off the alternate syntax with the %%s
for /L %%A in (1,1,1) do (
set playerlevel=5
set x=player
call echo %%!x!level%%
)
You can get what you want by using a combination of enabledelayedexpansion and the set /a to emulate an EVAL function
Try this:
#echo off
setlocal enabledelayedexpansion
set playerlevel=5
set x=player
set /a varx = "%x%level"
echo %varx%

Windows Batch file processing - Loops

Am very new to batch scritping. I just tried to execute the below code for which am totally confused.
#echo off
set m=100
set i=0
FOR /L %%N IN (0,1,%m%)do (
set /a i=1+%i%
)
echo %i%
pause
Here, am incrementing i value until N reaches 100. So, am expecting the value of i at the end of loop to be 101 but it shows 1. Can anyone explain what's the reason behind this.
thanks
You need delayed expansion : http://www.robvanderwoude.com/variableexpansion.php
#echo off
setlocal enableDelayedExpansion
set m=100
set i=0
FOR /L %%N IN (;;0,1,%m%;;) do (
set /a i=1+!i!
)
endlocal & set /a i=%i%
echo %i%
In batch files, variable reads are replaced with their values at parse time, before executing the line or the block (code enclosed in parenthesis).
So, the %i% reference inside the for loop is replaced with the value of the variable before the for loop, that is, 0 , and %m% is replaced with 100, and what gets executed is
for /l %%n in (0 1 100) do ( set /a i=1+0 )
You can enable delayed expansion (setlocal enabledelayedexpansion command) and change the sintax from %i% to !i! to indicate the parser that reads to the variable should be delayed until the moment of executing the line. The code should be
#echo off
setlocal enabledelayedexpansion
set m=100
set i=0
FOR /L %%N IN (0,1,%m%)do (
set /a i=1+!i!
)
echo %i%
endlocal
The read of the value of i inside the for loop is delayed. This way, the real value is readed just before each execution of the line. Reference to i out of the loop does not need it. When the line is reached and parsed, the correct value is retrieved and echoed.
Anyway, while this is the general rule, set /a has a different behaviour. If you change your code to
set /a i+=1
or
set /a i=i+1
the parser will correctly identify the operation and execute the expected code with or without delayed expansion enabled.

Splitting string using delayed expansion limitation

When I was testing a script I came across this issue when trying to extract characters from a string using batch. I have simplified it into a simple example. t.txt just contains the word hello.
#echo off
setlocal enabledelayedexpansion
set a=0
set b=1
for /f %%a in (t.txt) do (
set x=%%a
echo !x:~!a!,!b!!
set /a x+=1
)
pause >nul
The problem is, the variable x needs to be accessed using delayed expansion, and because I am updating the values of a and b through the loop these also need to be accessed using delayed expansion.
When trying to use the variables a and b to split the string they all need delayed expansion, but the order of the ! marks means that it is not parsed the way I intended!
CMD will expand my command as !x:~!, !,! and !!, instead of expanding the inner ones first. Obviously I can't use %'s either.
The only way I have found to get around this is to call an external function that isn't in the loop, so I can use %'s.
#echo off
setlocal enabledelayedexpansion
set a=0
set b=1
set v=
for /f %%a in (t.txt) do (
set x=%%a
call :RETURN x
set /a x+=1
)
pause >nul
:RETURN
set v=%1
echo %v:~!a!,!b!%
Is there any way of getting cmd to parse my command how I need it to, or this just a limitation I will have to use call for?
Simply transfer variables a and b to FOR variables.
#echo off
setlocal enabledelayedexpansion
set a=0
set b=1
for /f %%a in (t.txt) do (
set "x=%%a"
for /f "tokens=1,2" %%A in ("%a% %b%") do echo !x:~%%A,%%B!
REM this line makes no sense if x=hello: set /a x+=1
)
pause >nul
Mixing delayed and normal expansion will work.
#echo off
setlocal EnableDelayedExpansion
set a=0
set b=1
for /f %%L in (t.txt) do (
set "x=%%L"
echo !x:~%A%,%B%!
)

Resources