Windows batch and delayed variable expansion.

Captain Ron

Well-known Member
I'm trying to get my head around delayed variable expansion in DOS batch.

Here's two example scripts, each followed by their output.
I am trying to understand why the second one works precisely and the first doesn't.

What I don't get is why I have to do the !PREVEND! in the second script for that variable only and not for ALL the variables.
If anyone can explain this to me in plain English (for the batch noob I am) I would greatly appreciate it.
There are a lot of batch experts who understand the concept but I havn't found any who can put it into words I can understand!

@echo off for /l %%i in (0,10,106) do call :outputdata %%i call :outputdata 106 goto end :outputdata if not %1==0 ( set /a PREVEND=%1-1 echo START=%PREVSTART% echo END=%PREVEND% ) set /a PREVSTART=%1 :end START=0 END= START=10 END=9 START=20 END=19 START=30 END=29 START=40 END=39 START=50 END=49 START=60 END=59 START=70 END=69 START=80 END=79 START=90 END=89 START=100 END=99 @echo off setlocal enabledelayedexpansion for /l %%i in (0,10,106) do call :outputdata %%i call :outputdata 106 goto end :outputdata if not %1==0 ( set /a PREVEND=%1-1 echo START=%PREVSTART% echo END=!PREVEND! ) set /a PREVSTART=%1 :end START=0 END=9 START=10 END=19 START=20 END=29 START=30 END=39 START=40 END=49 START=50 END=59 START=60 END=69 START=70 END=79 START=80 END=89 START=90 END=99 START=100 END=105
 
Last edited:

EndlessWaves

Distinguished Member
I know little of this particular language and you don't explain what your intended result is but looking up the assignment operators I think I see what you're after.

Would it be fair to say that the end output value is being delayed one loop from what you intend?

It appears as if the language has a sort of lazy parser where everything within a set of brackets is evaluated before it's run.

So the code

if not %1==0
(
set /a PREVEND=%1-1
echo START=%PREVSTART%
echo END=%PREVEND%
)

becomes

if not 100==0
(
set /a PREVEND=100-1
echo START=90
echo END=89
}

And then it's executed.

Your global switch and alternative variable assignment makes it behave more like most languages where variable are checked every time they're used.


In this example I'd probably rewrite the starting conditions to get rid of the need for an if statement, resulting in something like:

Code:
@echo off

set StartingValue=0
set Increment=10
set FinalValue=105

set PREVSTART=%StartingValue%
set /a StartingValue=%StartingValue% + %Increment% - 1

for /l %%i in (%StartingValue%,%Increment%,%FinalValue%) do call :outputdata %%i

call :outputdata %FinalValue%

goto end

:outputdata
  echo START=%PREVSTART%
  echo END=%1
  set /a PREVSTART=%1 + 1

:end
 

Captain Ron

Well-known Member
I simplified the script to illustrate the problem with the variable parsing. The actual use is for processing chapter times for dvd/bluray and the second example is esentially the logic that works perfectly in my full script. I had to write it to output the previous chapter details when you have the start time of the next chapter because you don't know the end time of any given chapter until you have the start time of the next chapter when reading the file from start to finish. Your example is a far tidier way to do the specific loop logic as shown in my example above but that's not what I am after. I am after an understanding of precisely why the first one doesn't work. Also, if you look at my first example the value output for START is correct the whole way down. Only the value for END is screwy and particularly wierdly it's like all the values have been rolled on one iteration and even wierder the first value output is the very last one which confuses me even more! Having a script that works is great but understanding why it works is more important should I ever run into this delayed expansion weirdness again.
 

EndlessWaves

Distinguished Member
Also, if you look at my first example the value output for START is correct the whole way down. Only the value for END is screwy and particularly wierdly it's like all the values have been rolled on one iteration and even wierder the first value output is the very last one which confuses me even more!

It's printing to console whatever the value is when you open brackets on the If statement, so you don't see the updated value reflected until the next time around.

So you get the intended value for start because you haven't changed it, but the pre-bracket value for end.

You're seeing 105 because you've previously defined PREVEND to be that value, my output from just the snippet you've posted is:

START=0
END=
START=10
END=9
...

It might help if you think of all statements within a set of brackets as being executed simultaneously, with none of them affecting any of the others, instead of sequentially like normal.
 

Captain Ron

Well-known Member
105 isn't assigned anywhere and is only passed to output_data after the loop completes, it's not even passed by the loop so how does it appear in the first iteration of printing? I know I am obviously missing something fundamental here but I havn't grasped it yet.
 
Last edited:

EndlessWaves

Distinguished Member
So you get that result even if you copy from the forum post into a new text file and run that?

It's not showing up on mine so it's not in the code posted. Maybe you've ended up setting it as a environment variable or something?
 

Captain Ron

Well-known Member
You were right. I have variable contamination from prior runs and THAT's what was confusing me. I get blank in the END output on the first iteration for the first version if I run it in a fresh shell. Now the light of reason is restored. I need to unset the variables at the end of each run. Thankyou! :)
 

The latest video from AVForums

Podcast: Samsung TV Launch & QN95A Neo QLED Review, plus Film & TV news & Reviews
Subscribe to our YouTube channel
Support AVForums with Patreon

Top Bottom