0

Expected Input: Start=1, End=500, Interval=100

Expected Output:

1, 100

101, 200

201, 300

301, 400

401, 500

@echo off
set start=1
set end=500   
set interval=100
for /L %%g in (%start%, %interval%, %end%) do (
set first=%%g
set /a last=%first% -1 + %interval%
echo %first% , %last%

)

Actual output:

401 , 401 -1 + 100

401 , 401 -1 + 100

401 , 401 -1 + 100

401 , 401 -1 + 100

401 , 401 -1 + 100

Not sure why first is assigned with 401 value, Appreciate any help

Vinayak Dornala
  • 111
  • 1
  • 3

2 Answers2

5

This is a classic delayed expansion issue. Type help set or set /? from the command prompt for a discussion about delayed expansion (a bit past half way down the full help).

Normal expansion using %var% occurs when the line is parsed. The problem is your entire FOR loop block is parsed in one pass. So you are seeing a constant value that existed before the loop started. The value of 401 is probably left over from a prior run.

The solution is delayed expansion. First you must enable it by using setlocal enableDelayedExpansion. Then you use !var! instead of %var%. This will give the value at execution time instead of at parse time.

Also, there is no need to expand numeric variables when using set /a.

@echo off
setlocal enableDelayedExpansion
set start=1
set end=500
set interval=100
for /L %%g in (%start%, %interval%, %end%) do (
  set /a first=%%g, last=first-1+interval
  echo !first!, !last!
)
dbenham
  • 651
  • 4
  • 12
  • +1 - You beat me. >smile – Evan Anderson Nov 27 '14 at 02:34
  • @EvanAnderson - I'm normally over at StackOverflow. But every now and then a ServerFault question sneaks into my "batch" question filter. – dbenham Nov 27 '14 at 02:38
  • I have a real soft spot for batch... I use it for a lot of stuff that I really shouldn't be because it's fun. For perverse values of "fun"... – Evan Anderson Nov 27 '14 at 02:40
  • @EvanAnderson - Windows batch happens to be my primary perverse hobby :-) to go along with my more main stream music hobby. You might find the following examples interesting: [Throttled parallel processing of a list of tasks using batch](http://stackoverflow.com/a/11715437/1012053), [SNAKE.BAT - An arcade style game using pure batch](http://www.dostips.com/forum/viewtopic.php?f=3&t=4741), and [Colossal Cave Adventure in batch](http://www.dostips.com/forum/viewtopic.php?f=3&t=4876). – dbenham Nov 27 '14 at 03:44
  • I'm definitely going to have to read your scripts in detail. With just a quick perusal I'm already seeing some fun little bits I'll have to co-opt for my own use. (I'm really liking the bit in SNAKE that puts a carriage-return in an environment variable, for example.) Your level of mastery of batch is _really_ impressive, and I'm actually a little embarrassed to have my answer sitting beside yours... >smile< You are driving nails with a screwdriver *very* precisely and accurately. – Evan Anderson Nov 27 '14 at 03:57
3

@dbenham is right on. I'll throw out an alternative. I find the delayed expansion a bit ugly and confusing for longer subroutines, so I'll tend to have a lone call as the "body" of a for loop. Inside the call variable expansion happens "normally". Observe:

@echo off
setlocal
set start=1
set end=500   
set interval=100
for /L %%g in (%start%, %interval%, %end%) do call :_d %%g
endlocal
goto :EOF

:_d
set /a last=%1 - 1 + interval
echo %1, %last%

For this particular case it's more code, to be sure, but for less trivial batch files I think it's a "win".

As an aside: You have no setlocal / endlocal in there, so the values for first and last will persist across executions, assuming you keep running them in the same shell, and give you different results the second time you run your code. I tend to wrap my batch files in a setlocal / endlocal pair to prevent variables I use in the script from "leaking" out into the parent shell's environment.

Finally, I'll echo what @VasiliSyrakis says: If you can avoid using cmd.exe then, by all means, avoid it. I tend to write a lot of stuff in batch but, admittedly, it's a bit like hammering nails with a screwdriver for a lot of problems.

Evan Anderson
  • 141,881
  • 20
  • 196
  • 331
  • +1, That is certainly another way to do it. And for many (majority?) of scenarios, it makes sense. However, CALL is extremely SLOW. Not an issue here, but if the loop has many iterations, the impact of using CALL instead of delayed expansion can be painfully obvious. – dbenham Nov 27 '14 at 02:45
  • @dbenham - Is there *anything* "not SLOW" about batch, though? >smile – Evan Anderson Nov 27 '14 at 03:13
  • One nice thing about SETLOCAL is that there is an implicit ENDLOCAL for each active SETLOCAL whenever a script or CALLed routine exits. So the ENDLOCAL is often not needed. There are a few obscure exceptions: for example, a fatal syntax error can break the implicit ENDLOCAL. Of course an explicit ENDLOCAL would do no better in such a case :-) – dbenham Nov 27 '14 at 04:10