Read Mode
Default

How to use SETLOCAL & ENDLOCAL + DelayedExpansion in batch | Batch Basics| by Sounak@9434

Hello everyone. Its been a while, TheBATeam has not posted anything about the deep and important Basics of the Batch Programming. Although, Most of the basics seems to be easy and simple. While some of them are quite Complex. And, they are quite hard to grasp. So, This time we're not going to publish a new tool but want to show you some tools inbuilt in CMD. If you'll understand the usage and importance of these tools in batch. You can easily make quite advanced Batch programs.

These two special commands are provided by Microsoft. Because of few problems arising while coding in batch. The commands are Setlocal and Endlocal. As the name of these commands are mysterious - So, does their working. Thus, Today we'll know about few limitations of Batch programming. And, How we can eliminate these limitations? Using these awesome commands. Let's have a basic look at the working of both of these commands.

Use 'SETLOCAL' & 'ENDLOCAL' + DelayedExpansion

Whenever you'll analyze the code of a great batch programmer. Most of the time You'll find a line of code written as 'Setlocal Enabledelayedexpansion' in the main code. On the other hand, It is quite difficult to know about the working of this line in the code. So, Today - I'm going to tell you about this and the tricks and benefits related to this Command Syntax. Hold on with me. We're about to Take off. 😏

SETLOCAL😕

Setlocal is a command that sets variable in a local environment. In simple words, It make all your variables to Local, and if you'll Execute 'Endlocal'. All the variables you've created after calling 'Setlocal' will be removed from the RAM. That means variables set within a setlocal environment won't affect variables out of that part.
An example might make this thing clear but we have to learn about endlocal first to do that.

ENDLOCAL😑

Endlocal is a command that ends the local environment, set by using the setlocal command. And, returns to the previous environment. Thus, It can protect the variables from being over-write multiple times within the program. An, Example may be better in understanding these concepts. Then let's have a basic example. 😄

Don't worry, all files are provided in a zip archive to download about this article.


-------------START OF BATCH CODE-------------
@echo off
set var=This is first text
set var
setlocal
echo setlocal
set var=This is second text
set var
setlocal
echo setlocal
set var=This is third text
set var
endlocal
echo endlocal
set var
endlocal
echo endlocal
set var
>nul pause
-------------END OF BATCH CODE-------------

Use of setlocal and endlocal

CONCLUSION😆

So we can see in the example that values changed inside a setlocal is not affecting values outside the setlocal. We can also derive that setlocal can be recurred i.e. used one inside another. By the way the maximum number of setlocal recursion is 32. So, Technically - we can only make 32 versions of the variable having the same name inside a program. But, I Really prefer keeping different names for the variables. As, it reduces complexity and helps in understanding the program later.

USAGE😊

After learning this feature you must be wondering what is the use of this feature? Because, what is the benefit of knowing about these commands, if there's no practical use of them in your programs. So, following are the 2 examples of using them for the sake of your understanding. I hope, they'll help you out. 😕





1) USE IN REUSABLE CODE:

Here is a simple script that reverses the input. Here, I'm using a Separate Module to perform a specific function in the program. So, I can call this module multiple times with multiple parameters. This makes my program more flexible. i.e. it can do more work in less amount of coding lines. Smart hann! 

-------------START OF BATCH CODE-------------
@echo off
call :reverse Will it really work
call :reverse 1 2 3 4 5 6 7 8 9
exit /b

REM This is a Batch Module. (Kind of Function)
:reverse
set _data=%1 %_data%
shift
if "%1" neq "" goto reverse
echo %_data%
Goto :eof
-------------END OF BATCH CODE-------------

Output without setlocal
Now, If you are working on a important project and spend hours on it to finally see this type of (WRONG) output. it is only human to get frustrated and leave as it is for some time.

So what can we do now to fix it?
Among many different ways one is shown here.
Change the code as follows:-
-------------START OF BATCH CODE-------------
@echo off
call :reverse Will it really work
echo(
call :reverse 1 2 3 4 5 6 7 8 9
exit /b

:reverse
if not defined _data setlocal
set _data=%1 %_data%
shift
if "%1" neq "" goto reverse
echo %_data%
endlocal
-------------END OF BATCH CODE-------------

Output with setlocal - Tool Fixed :)

2) USE IN DELAYEDEXPANSION:

To understand this topic we have to know about delayedexpansion first. It is the most important basic of the batch programming. If you've understood the For Command of the CMD, then this will be the second most important command to learn. Have a look at it.

DELAYEDEXPANSION😕

What is delayed-expansion?
>>>Delayed-expansion is a feature that tells CMD to also expand variables in !var! form, instead of %var% form. So, why do we need to use variables with !(exclamation mark) where we can use the basic %(percent sign) ? This must be the first question that came to your mind, after reading above. So, To understand this we have to understand how CMD executes a batch code. (I have only provided a basic info, for advanced info look hereCMD executes a code in two main phases.

1)Read phase
2)Execute phase
Related post: WTF Diffrence Between '%VAR%' & '!VAR!' ? - By Kvc

1)READ PHASE

>>>On the first part of read phase CMD either reads a whole line of the batch code or CMD reads a Code block. Code block is a set of codes inside a parenthesis '(...code...)'. N.B. Codes inside a parenthesis would only be considered a code block if CMD is looking for code token. So,

-------------START OF BATCH CODE-------------
del something (
    morethings
)
-------------END OF BATCH CODE-------------

would parse each line one by one. Whereas,

-------------START OF BATCH CODE-------------
if 5 neq 10 (
    echo Main
    echo Name
)
-------------END OF BATCH CODE-------------

would parse the total code block in once. You can understand it easily by having 'echo on'. On the second part of read phase CMD expands all variable with % to their respective data. This is the main part that generates the problem as well as comes in handy some times. We would have an example but before that we need to learn about Execute Phase.

2)Execute Phase

>>>In this phase CMD takes in the output of read phase. Then expands the variables with !(exclamation sign) and executes the code. I know, it is quite difficult to understand this via reading about it. And example is always better than a 1000 words of explanation. So, Lets take an example to make it clearer.

-------------START OF BATCH CODE-------------
setlocal enabledelayedexpansion
set "data1=I am data 1"
echo %data1%
echo !data1!

set "data2=I am not data 2"
set "data2=I am real data 2"&echo %data2%&echo !data2!

set "data3=I'm not at all data 3"
if 5 equ 5 (
    set "data3=I am data 3"
    echo %data3%
    echo !data3!
)
-------------END OF BATCH CODE-------------

Output

With the output we can easily guess what is happening behind the scenes. I haven't used '@Echo off' because, you can easily see and understand what is going on with the code. Because, Once you'll understand this concept. you'll start making your own innovative and amazing programs.

-------------UNDERSTANDING THE CODE-------------

1) Setlocal EnableDelayedExpansion
>>> The first line setlocal enabledelayedexpansion enables delayed-expansion for further use. So we can now use !var! form of variables. Which is quote useful in other cases too. e.g. within the For Loop.

2) set "data1=I am data 1"
>>> The second line sets variable named data1 to "I am data 1"

3) echo %data1%
>>> On the first part of read phase CMD reads the line as "echo %data1%".
>>> On the second part of read phase expands %data1% to "I am data 1" and the command becomes "echo I am data 1"
>>> On the execute phase cmd executes the code "echo I am data 1".

4) echo !data1!
>>> On the first part of read phase it reads 'echo !data1!' and on the second part it remains the same 'echo !data1!'
>>> On the execute phase CMD expands !data1! to 'I am data 1' and then runs the code which echoes 'I am data 1'

5) set "data2=I am not data 2"
>>> This line sets data2 to "I am not data 2"

6) set "data2=I am real data 2"&echo %data2%&echo !data2!
>>> On the first part of read phase CMD reads the whole line. Thus the command to execute is 
'set "data2=I am real data 2"&echo %data2%&echo !data2!'
>>> In this part of read phase CMD expands the variable %data2%. As this line of code is still not executed the contents of data2 is still "I am not data 2". So in the second part of read phase the command to execute becomes 
set "data2=I am real data 2"&echo I am not data 2&echo !data2!
 as seen in the CMD window if echo is on.
>>> In the execute phase CMD takes in the output of read phase and then executes command from left to right and thus 'set "data2=I am real data 2"' gets executed at first. Then echoes I am not data 2 and finally it expands !data2! to 'I am real data 2' and echoes it to CMD window.
Also Read : Dynamic Vs. Static Coding Concept. - Advanced Batch Programming
7) set "data3=I'm not at all data 3"
>>> This line sets data3 to "I'm not at all data 3"

8) if 5 equ 5 (
    set "data3=I am data 3"
    echo %data3%
    echo !data3!
)
>>> As it is a code block in the first part of read phase CMD reads this whole part.
>>> In the second part of read phase CMD expands the variable %data3% to its value. And as this piece of code is not executed yet thus the value of %data3% is still "I'm not at all data 3". So the code to execute becomes:-
if 5 equ 5 (
    set "data3=I am data 3"
    echo I'm not at all data 3
    echo !data3!
)
As seen in the CMD window.
>>> Now in the execute phase CMD takes in the output of read phase and executes commands from up to down and thus at first data3 is set to "I am data 3", then it echoes I'm not at all data 3 and then while executing 'echo !data3!' it expands !data3! which is now 'I am data 3' and echoes it to CMD window.
----------------------------------------------------
So this is what happens behind the scenes and this is also the main difference between %var% and !var!. One is executed while reading and the other is executed while executing.

Let's have another example:-
-------------START OF BATCH CODE-------------
@echo off
set /p num=Enter any number:
set /a numchk=%num%0 2>nul||goto :wrong
:: Using || is a special syntax which runs a code if the previous one fails
echo(
::Generating a table
echo Generating the multiplication table of this number
for /l %%a in (1,1,10) do (
    set /a tabnum=%num%*%%a
    echo %num%x%%a=%tabnum%
)
echo(
::Generating factorial
echo Generating factorial of this number
echo(
if %num% gtr 12 goto :error
set factnum=1
for /l %%a in (1,1,%num%) do (
    set /a "factnum=%factnum%*%%a"
)
echo Factorial of %num%: %factnum%
pause>nul
exit /b
:error
echo Sorry, number is too big for CMD to calculate it's factorial
pause>nul
exit /b
:wrong
echo(
echo Sorry, either your number is too big for CMD to calculate
echo Or, it contains non-numeric character
pause>nul
exit /b
-------------END OF BATCH CODE-------------


Can you fix the code? 😉

  1. ::TIPS
  2. Setlocal enabledelayedexpansion enables !var! format within a new local environment.
  3. All variables set before setlocal are available inside a setlocal too.
  4. All variables set or changed inside a local environment will be reverted back to their last value upon endlocal.
  5. setlocal disabledelayedexpansion and endlocal both may be used to stop the use of !var! format.


Keep Learning, Keep Sharing...
Be Happy, Spread Happiness...

Download Link: Download test files
#TheBATeam

1 comment:

Powered by Blogger.