Friday 25 July 2008

Batch programming tip#13: Using setlocal enabledelayedexpansion

Delayed expansion is pretty tricky to explain. It basically means you are making sure that your variable is evaluated as many times as necessary and not just once (as would normally be the case).

The typical use for this is when using a "for" command. Without delayed expansion, variables do not appear to get set correctly. Why? Because they are evaluated once for the for command (rather than each time you loop) which results in apparently strange behaviour.

Example:

@echo off

:action
set num=0
for /l %%N IN (1,1,9) do (
echo %num%
set /a num=%num%+1
)
goto show_result

:show_result
echo Num is actually now worth: %num%
goto eof

:eof
echo Press any key to quit...
pause > NUL
goto blackhole

:blackhole

The for /l %%N IN (1,1,9) do command basically creates a loop from 1 to 9 with a step of 1 (so nine passes).
Anyhow, anyone would go crazy trying to debug this thing: it outputs nine zeros and a total worth of 1!

Enter delayed expansion however and things get back to normal:

@echo off

:init_sys
setlocal enabledelayedexpansion

:action
set num=0
for /l %%N IN (1,1,9) do (
echo !num!
set /a num=!num!+1

)
goto show_result

:show_result
echo Num is actually now worth: %num%
goto eof

:eof
echo Press any key to quit...
pause > NUL
goto blackhole

:blackhole

Notice we use the exclamation mark instead of % to reference variables within the for loop. This makes sure the variable is reassessed every time we use it.

I have written a short example below with a loop inside a loop. It loops through a sentence and shows every word separately using a call to an appropriate subroutine.


@echo off

:init_sys
setlocal enabledelayedexpansion

:init_vars
set text=The quick brown fox jumps over the lazy dog

:action
echo Displaying text using %%: %text%
echo Displaying text using ^^!: !text!

for /l %%N IN (1,1,9) do (
call :get_substr %%N "%text%"
echo Token: !strtok!
)
goto eof

:get_substr
set token=%~1
set string=%~2

for /F "usebackq tokens=%token% delims= " %%w in ('!string!') do (
set strtok=%%w
)
goto blackhole

:eof
echo Press any key to quit...
pause > NUL
goto blackhole

:blackhole

Note that if you don't use the delayed expansion properly (e.g. in the second for loop: '!string!') all hell will break loose... I know because I tried :D

The second use of delayed expansion is to nest variables like so:

@echo off

:init_sys
setlocal enabledelayedexpansion

:init_vars
set text=The quick brown fox jumps over the lazy dog

:action
for /l %%N IN (1,1,9) do (
call :getchars %%N
)
goto eof

:getchars
set num=%~1
echo !text:~0,%num%!
goto blackhole

:eof
echo Press any key to quit...
pause > NUL
goto blackhole

:blackhole

What this script does is successively retrieves N (N being 1 to 9) number of characters from our sentence "The quick brown fox jumps over the lazy dog".
To do this we nest the %num% variable in our "substring" notation:
!text:~0,%num%!

Cool.

Thoughts?

No comments:

Online Marketing
Add blog to our blog directory blog search directory Blog Directory Blogarama - The Blog Directory