Windows Batch file processing - Loops - batch-file

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.

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!
)
)

Batch file - loop through first n lines of text file

I'm trying to determine the format of a text file by looping through the first 10 lines, perform some regex matching and then compare the results at the end. I can easily loop through the entire file, but I only want the first N lines (in this case 10)
I'm familiar with other languages, but the idiosyncrasies of this batch file is throwing me for a loop so to say.
Here is what I have so far:
#echo off
setlocal enableDelayedExpansion
set /A REGEXCOUNTER=0
set /A COUNTER=0
for /F %A in (%submitfile%) do (
set /A COUNTER=COUNTER+1
rem echo %A
setlocal enableDelayedExpansion
echo(%A|findstr /r /c:"[0-9].*" >nul && (
set /A REGEXCOUNTER=REGEXCOUNTER+1
echo %COUNTER% - %REGEXCOUNTER% - FOUND - %A
rem any commands can go here
) || (
echo NOT FOUND
rem any commands can go here
)
rem LOOP END
if %COUNTER% GEQ 10 do (goto loop_over)
)
)
:loop_over
echo "END HERE!"
I've got counters set up that incrementally tick up to count my matches and how many times it's looped. However here is some sample output of variable values:
110 - 0 - FOUND - 003
220 - 0 - FOUND - 2
330 - 0 - FOUND - 1
440 - 0 - FOUND - 029
The loop counter variable is increasing by ten for each loop and the regex match counter is not going up at all. I'm pretty sure this has something to do with variable scope but I'm not sure where to begin.
This should fix all the issues I talked about in my comments above.
#echo off
setlocal enableDelayedExpansion
set /A REGEXCOUNTER=0
set /A COUNTER=0
for /F %%A in (%submitfile%) do (
set /A COUNTER=COUNTER+1
rem echo %%A
echo(%%A|findstr /r /c:"[0-9].*" >nul && (
set /A REGEXCOUNTER=REGEXCOUNTER+1
echo !COUNTER! - !REGEXCOUNTER! - FOUND - %%A
rem any commands can go here
) || (
echo NOT FOUND
rem any commands can go here
)
rem LOOP END
if !COUNTER! GEQ 10 goto loop_over
)
)
:loop_over
echo "END HERE"
The extra setlocal within the loop should be removed. Once setlocal enabledelayedexpansion has been executed, it remains in effect until a setlocal disabledelayedexpansion is executed or until the batch terminates.
Each %A (the loop-control metavariable must be %%A (one % if run from the command-line, two if within a batch file)
If you change the value of a variable within the loop, then you need to refer to the changed value as !varname!, not %varname% which is the original value (search SO for delayed expansion). set /a always works on the current variable value.

batch programming

I am new in batch. Trying for some days to make something in batch but have a problem I cannot solve. I read a lot of your comments but did not find answer. Maybe you can help me?
The point is:
I input string from keyboard( e.g. 10 characters ). name of it is"allinputstring"
Calculate of length is ok ( by redirect in txt file and expand its bytes ). name "length"
Parse string in 10 pieces (strings) is ok.
So here is a problem, I want to echo these pieces, so I use next code, I use a counter to find out is the counter give me good count as output variable, and echo it to see on screen if it is good. Counter seems good, end echo of pieces strings is good enough. But I want to put in line 5. Variable count instead of "%%m", and cannot find a syntax way how to do it.
setlocal enabledelayedexpansion
for /l %%m in (1,1,!lenght!) do (
set /a count=0
set /a count=count+%%m
echo !count!!allinputstring:~%%m,1!
)
endlocal
please help me.
Try this:
#echo off &setlocal enabledelayedexpansion
set /a lenght=9
set "allinputstring=ABCDEFGHIJ"
for /l %%m in (0,1,%lenght%) do (
set /a count=0
set /a count+=%%m
echo !count! !allinputstring:~%%m,1!
)
endlocal
Output is:
0 A
1 B
2 C
3 D
4 E
5 F
6 G
7 H
8 I
9 J
#ECHO off
setlocal ENABLEDELAYEDEXPANSION
SET allinputstring=abcdefghijk
SET lenght=10
for /l %%m in (1,1,!lenght!) do (
set /a count=0
set /a count=count+%%m
FOR %%z IN (!count!) DO echo !count! !allinputstring:~%%z,1!
)
GOTO :eof
Does this do what you require?
So... to make COUNT show (I've assigned it to KOWNT, but the syntax endlocal&set count=%count% would assign it to COUNT instead)
I've changed the starting value of the FOR/L because character counting starts from character#0 in the string.
#ECHO off
setlocal ENABLEDELAYEDEXPANSION
SET allinputstring=abcdefghij
SET lenght=9
for /l %%m in (0,1,!lenght!) do (
set /a count=0
set /a count=count+%%m
FOR %%z IN (!count!) DO echo !count! !allinputstring:~%%z,1!
)
endlocal&SET KOWNT=%count%
ECHO Now KOWNT=%KOWNT% but count=%count% because we have exited the SETLOCAL
GOTO :eof
When the ENDLOCAL is encountered, the parser substitutes the CURRENT value of the variables in the line and THEN executes the line.
Hence, the line is executed as
endlocal&set KOWNT=9
since the value of count at the time is 9.
When the SETLOCAL is executed, all changes to the environment since the matching SETLOCAL are thrown away. The environment variables are restored to their state when the SETLOCAL was executed and count becomes empty again (as it was before the routine.) THEN the SET instruction is executed, which sets KOWNT to 9.

Why won't this batch code work?

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.

Arithmetic inside a for loop batch file

I have a for loop in a batch file that looks like this:
for %%y in (100 200 300 400 500) do (
set /a x = y/25
echo %x%
)
The line:
set /a x = y/25
Doesn't seem to be doing any division. What is the correct syntax for dividing each y by 25? I only need the integer result from this division.
Environment variables need not be expanded to use in a SET /A statement. But FOR variables must be expanded.
Also, even if your computation worked, the ECHO would fail because percent expansion takes place when a statement is parsed, and the entire FOR construct is parsed at once. So the value of %x% would be the value as it existed before the loop is executed. To get the value that was set within the loop you should use delayed expansion.
Also, you should remove the space before the assignment operator. You are declaring a variable with a space in the name.
#echo off
setlocal enableDelayedExpansion
for %%A in (100 200 300 400 500) do (
set n=%%A
REM a FOR variable must be expanded
set /a x=%%A/25
REM an environment variable need not be expanded
set /a y=n/25
REM variables that were set within a block must be expanded using delayed expansion
echo x=!x!, y=!y!
REM another technique is to use CALL with doubled percents, but it is slower and less reliable
call echo x=%%x%%, y=%%y%%
)
It's not doing anything because "y" is just a letter. You need percent signs to reference the variable.
set /a x = %%y/25
I was having the same issue but turned out to be an integer issue. I was multiplying after dividing but need to before. What was happening is something like this:
1/100x100 which operates like 1\100=0 then 0x100=0
I changed it to
1x100/100 which operates like 1x100=100 then 100/100=1
#echo off
setlocal ENABLEDELAYEDEXPANSION
for /f "usebackq" %%b in (`type List.txt ^| find "" /v /c`) do (
set Count=%%b
)
)
REM Echo !Count! -->Returns the correct number of lines in the file
for /F "tokens=*" %%A in (List.txt) do (
set cName=%%A
set /a Number+=1
REM Echo !Number! -->Returns the correct increment of the loop
set /a Percentage=100*!Number!/!Count!
REM Echo !Percentage! -->Returns 1 when on the first line of a 100 line file
set a=1
set b=1000
set /a c=100*1/100
Rem -->echo c = !c! --Returns "C = 1"
)

Resources