China DOS Union

-- Unite DOS · Advance DOS · Grow DOS --

Union site: www.cn-dos.net Forum site: www.cn-dos.net/forum
DOS stands for freedom, openness and progress. Let us work hard, learn from the openness and GNU spirit of FreeDOS and Linux, and together build and grow a free GNU GPL world!

中国DOS联盟论坛
The time now is 2026-07-31 14:38
中国DOS联盟论坛 » DOS批处理 & 脚本技术(批处理室) » The mechanism of delayed environment variables can be perfectly explained from the perspective of the stack View 1,108 Replies 9
Original Poster Posted 2009-03-01 04:33 ·  中国 贵州 遵义 电信
初级用户
Credits 69
Posts 34
Joined 2009-02-08 23:47
17-year member
UID 138359
Gender Male
Status Offline
-------My---research on call echo-----------------------------------
1---the standard usage of call
Example 1:
call :a
goto:eof
:a
:echo 123
Example 2:
call :a 123
goto:eof
:a
echo %1

Combine examples 1 and 2, and write them in a nonstandard form:
Example 1------>call echo 123----echoes 123
Example 2----->call echo %1---if you save the file as nn.bat and enter nn 123 , then it echoes 123
Example 3-----call echo %%i ---echoes i---this shows that 2 % signs were stripped off, call and echo each strip off one
So call echo is actually shorthand for calling a subroutine. That makes it easy to explain why call has the function of delayed environment variables, but it does not actually
turn on delayed environment variables. It is only the result of calling a subroutine.
2-----an example of call inside for
for %%i in (0 1 2) do (
set t=%%i
echo %t%
)
Because bat is interpreted as it executes, in the end it only shows---->2

Now look at this:
call :a
goto:eof
:a
set t=123
for %%i in (0 1 2) do (
set t=%%i
echo %t%
)

It echoes---》123 123 123, because what cmd executes is an interpreted program.
Change it a bit
call:a %t%
goto:eof
:a
set t=123
for %%i in (0 1 2) do (
set t=%%i
echo %1
)

At this time call invokes the subroutine parameter, and it echoes----》2 2 2
The previous line used call to invoke the result from before the for executed, this line invokes the result after the for executed
From this, the conclusion is: if you want to invoke the result after the for executes, you should invoke the subroutine parameter.

Change the previous line a bit more
@echo off
set t=123
for %%i in (0 1 2) do (
set t=%%i
call:a
:a
echo %t%
)
At this time it shows----》0 123 1 123 2 123. This line shows that call can make the data pointer move together with the for statement while the for is executing, but after the for finishes, it returns to
its initial state. This function of call is equivalent to the function of delayed environment variables.
There is no way for this line to avoid pushing t=123 into the stack; no matter where you add goto:eof it won't work. This is because the pivot point call pushes the preceding data into the stack. Here call considers the preceding data to be t=123 , and after call it automatically pops the original data back out of the stack, so then we see this dance of data: 0 123 1 123 2 123. Unfortunately bat has no statement for controlling the stack. After countless people researched it, a pathological way of writing was found to solve this problem.
Change it again:

@echo off
set t=123
for %%i in (0 1 2) do (
set t=%%i
call echo %%t%%
)
At this time it echoes-----》0 1 2
The reason is this: the principle of call execution is that it first pushes the preceding result into the stack, then enters the branch point, and after executing the contents at the branch point, it pops the saved data back out of the stack, then returns to the next branch point after call and continues forward.
Although the statement above is pathological, that extra % sign just happens to anesthetize call. For that extra % sign, call thinks that the data it should push into the stack is the data from the start of the for, and on each loop of for , call pushes that loop's data into the stack. After call finishes executing the subroutine (that is, the echo %%t%% in this pathological form), it pops the data back out of the stack, then executes the next statement, which is to enter the next loop of for. In the call statement the data pointer %t% always points to storage unit t , and the value for each loop of for is stored in storage unit t. Thus the changing value of t can be seen dynamically. The method is pathological, but this is the only thing that can be done, since Microsoft never made a command to manage the stack. Delayed environment variables should also be using stack technology. The statement setlocal enabledelayedexpansion pushes the preceding data into the stack. endlocal pops the data that was pushed into the stack back out. %t% ,!t! are data pointers, pointing to storage unit t, so the data that call pushes into the stack is the data of for.



000This viewpoint is my own original creation. If anyone has a different opinion, discussion is welcome.

[ Last edited by 5yue5 on 2009-3-1 at 04:44 ]
Recent Ratings for This Post ( 2 in total) Click for details
RaterScoreTime
HAT +2 2009-03-01 04:43
yishanju +8 2009-03-02 03:50
Floor 2 Posted 2009-03-02 02:46 ·  中国 辽宁 大连 旅顺口区 联通
初级用户
Credits 50
Posts 19
Joined 2008-02-09 22:04
18-year member
UID 110396
Gender Male
Status Offline
1---the standard usage of call
Example 1:
call :a
goto:eof
:a
:echo 123
Example 2:
call :a 123
goto:eof
:a
echo %1

Combine examples 1 and 2, and write them in a nonstandard form:
Example 1------>call echo 123----echoes 123
Example 2----->call echo %1---if you save the file as nn.bat and enter nn 123 , then it echoes 123
Example 3-----call echo %%i ---echoes i---this shows that 2 % signs were stripped off, call and echo each strip off one

So call echo is actually shorthand for calling a subroutine. That makes it easy to explain why call has the function of delayed environment variables, but it does not actually
turn on delayed environment variables. It is only the result of calling a subroutine. (It is not that simple.)


2-----an example of call inside for
for %%i in (0 1 2) do (
set t=%%i
echo %t%
)
Because bat is interpreted as it executes, in the end it only shows---->2 (Is the code written wrong? The correct displayed result should be empty.)

Now look at this:
call :a
goto:eof
:a
set t=123
for %%i in (0 1 2) do (
set t=%%i
echo %t%
)

It echoes--->123 123 123, because what cmd executes is an interpreted program.

Change it a bit
call:a %t%
goto:eof
:a
set t=123
for %%i in (0 1 2) do (
set t=%%i
echo %1
)
At this time call invokes the subroutine parameter, and it echoes---->2 2 2 (Is the code written wrong? The correct displayed result should be empty. How should “invoke the subroutine parameter” be understood?)
The previous line used call to invoke the result from before the for executed, this line invokes the result after the for executed
From this, the conclusion is: if you want to invoke the result after the for executes, you should invoke the subroutine parameter. (I have no idea what this is talking about.)

Change the previous line a bit more
@echo off
set t=123
for %%i in (0 1 2) do (
set t=%%i
call:a
:a
echo %t%
)
At this time it shows---->0 123 1 123 2 123. This line shows that call can make the data pointer move together with the for statement while the for is executing, but after the for finishes, it returns to
its initial state. This function of call is equivalent to the function of delayed environment variables.
There is no way for this line to avoid pushing t=123 into the stack; no matter where you add goto:eof it won't work. This is because the pivot point call pushes the preceding data into the stack. Here call considers the preceding data to be t=123 , and after call it automatically

pops the original data back out of the stack, so then we see this dance of data: 0 123 1 123 2 123. Unfortunately bat has no statement for controlling the stack. After countless people researched it, a pathological way of writing was found to solve this

problem.

Change it again:

@echo off
set t=123
for %%i in (0 1 2) do (
set t=%%i
call echo %%t%%
)
At this time it echoes----->0 1 2
The reason is this: the principle of call execution is that it first pushes the preceding result into the stack, then enters the branch point, and after executing the contents at the branch point, it pops the saved data back out of the stack, then returns to the next branch point after call and continues

forward. (call may process data like this, but the key is “preprocessing”.)

Although the statement above is pathological, that extra % sign just happens to anesthetize call. For that extra % sign, call thinks that the data it should push into the stack is the data from the start of the for, and on each loop of for , call pushes that loop's data

into the stack. After call finishes executing the subroutine (that is, the echo %%t%% in this pathological form), it pops the data back out of the stack, then executes the next statement, which is to enter the next loop of for. In the call statement the data pointer %t% alw

ays points to storage unit t , and the value for each loop of for is stored in storage unit t. Thus the changing value of t can be seen dynamically. The method is pathological, but this is the only thing that can be done, since Microsoft never made a command to manage the stack. Delayed envi

ronment variables should also be using stack technology. The statement setlocal enabledelayedexpansion pushes the preceding data into the stack. endlocal pops the data that was pushed into the stack back out. %t% ,!t! are data pointers, poi

nting to storage unit t, so the data that call pushes into the stack is the data of for. (It cannot be determined whether this is really related to pushing and popping the stack.)

(As for “the mechanism of delayed environment variables,” because of the existence of the “preprocessing” phenomenon, the initial value of the variable may not be preserved, so I reserve my opinion.)
Recent Ratings for This Post ( 1 in total) Click for details
RaterScoreTime
zqz0012005 +4 2009-03-02 21:17
Floor 3 Posted 2009-03-02 12:10 ·  中国 广东 深圳 宝安区 电信
中级用户
★★
Credits 297
Posts 135
Joined 2006-10-21 12:00
19-year member
UID 67627
Gender Male
Status Offline
I only know that the usage of call is “to call another batch program from a batch program.”
As for call echo %%var%%, I generally understand it as meaning that there is a bat in memory whose content is echo %var% (why is it %var% and not %%var%%? That is the so-called “preprocessing” mechanism; see the great work by the hero xzyx at verybat.org).

Of course there is a stack when there is call, but what the stack saves is the current address (context protection), similar to functions in other languages. After the batch subroutine call finishes, the current address pointer is popped from the stack and the program continues executing. Pushing the address into the stack is done by the system; as for whether call pushes data into the stack, that still needs further research.

As for the mechanism of setlocal enabledelayedexpansion delayed environment variables, first we know that its function is to tell the command interpreter program (generally CMD.exe) not to perform variable substitution during preprocessing, and only expand them when the statement is executed. The system has to assign addresses to variables, but it does not necessarily have to use the stack, right? As for how it tells the preprocessor not to perform variable substitution, and how the system handles it, that also needs further research. (If readers are still very unclear about these concepts, please refer to the great work by the hero xzyx at verybat.org.)

The errors that xzyx pointed out in the OP's post are indeed errors. I don't know how the OP arrived at them.
Floor 4 Posted 2009-03-02 12:23 ·  中国 广东 广州 天河区 电信
金牌会员
★★★★
一叶枝头,万树皆春
Credits 2,564
Posts 1,127
Joined 2006-12-25 22:57
19-year member
UID 74552
Gender Male
Status Offline
Like most interpreted languages of this type under CMD, before a batch is executed line by line, there is a process of pre-reading and semi-compilation. Of course different languages handle this differently in detail. CMD is rather weak in this respect: it only reads the script text into memory, escapes certain symbols, marks breakpoints, and so on, then establishes the association between the in-memory text and the actual text lines, and then sends the commands in memory back to CMD again line by line according to the actual text line numbers for processing.
So in batch files, call echo %%i%% is call echo %i% at the command line.
This is not a characteristic of call, nor of for, it is simply CMD's
49206C6F766520796F752067757973 54656C3A3133383238343036373837
Floor 5 Posted 2009-03-02 12:23 ·  中国 广东 深圳 宝安区 电信
中级用户
★★
Credits 297
Posts 135
Joined 2006-10-21 12:00
19-year member
UID 67627
Gender Male
Status Offline
Before speaking, you should first look through the research results of the veterans.
echo %var% in for and call echo %%var%%
We should not forget that preprocessing reads the entire statement as one whole; a compound for statement is treated as one statement during preprocessing.
The current value of %var% is looked up in the previous statements.
This is also why set var=1&echo %var% cannot get the expected result either.
Floor 6 Posted 2009-03-02 12:25 ·  中国 广东 深圳 宝安区 电信
中级用户
★★
Credits 297
Posts 135
Joined 2006-10-21 12:00
19-year member
UID 67627
Gender Male
Status Offline
everest79 actually cut in ahead of me, and I wasn't finished yet. Hehe.
Floor 7 Posted 2009-03-02 22:45 ·  中国 辽宁 大连 旅顺口区 联通
初级用户
Credits 50
Posts 19
Joined 2008-02-09 22:04
18-year member
UID 110396
Gender Male
Status Offline
Da Diao is here too, hehe, let's discuss it together...
Floor 8 Posted 2009-03-03 01:40 ·  中国 广东 东莞 电信
银牌会员
★★★
批处理编程迷
Credits 1,916
Posts 752
Joined 2008-12-28 04:30
17-year member
UID 135147
Gender Male
From 广西
Status Offline
(Without turning on the so-called delayed variables) run this and see whether the result involves pushing onto the stack.
for %%a in (1,2,3,4) do (set/a a=a+%%a&call echo.%%a%%)

I also understand the situation as preprocessing variables with % signs, while variables without % signs are not affected.
If the so-called delayed variables are turned on, if you don't use ! signs, it still has no effect,
I understand it as ! variables not being expanded during preprocessing, and only looking up the variable's value when it is used,

What preprocessing does:
1. Expand variables inside % signs into the variable's value, then execute the command line with that value as part of the command line.
2. Merge every two consecutive % signs into one, and directly discard a single % by itself
3. Determine the final variable name for ! variables, but do not fetch the value, for example:
set a=k
!#%a%!during preprocessing, #%a% will not be directly treated as the variable name inside ! signs; instead after preprocessing: #k is the variable name inside ! signs. In fact there are two rounds of preprocessing here: 1 expand %a%, 2 determine that the variable name for ! is #k

Executing the command:
Treat the remaining % signs as characters rather than variables, and treat ! signs as variables and fetch their values.
For example:
call echo.#!a! and call echo %%a%% after preprocessing, during command execution they are actually:
call echo #!a! and call echo %a% after call the resulting commands are:
echo #k and echo %a%, rather than: echo #!a! and echo k
At this point preprocessing is performed again, yielding:
echo #k and echo k

[ Last edited by netbenton on 2009-3-3 at 00:18 ]
精简
=> 个人网志
Floor 9 Posted 2009-03-03 06:47 ·  中国 广东 深圳 南山区 电信
中级用户
★★
Credits 297
Posts 135
Joined 2006-10-21 12:00
19-year member
UID 67627
Gender Male
Status Offline
How did the poster above turn this into a discussion of preprocessing... Reading your explanation is exhausting, and it's exhausting for the reader too.
The preprocessing mechanism has already been analyzed very clearly by earlier people, go dig through the old threads (the classic thread collection, or just go to bbs.verybat.org and search for xzyx)
Floor 10 Posted 2009-04-08 09:09 ·  中国 浙江 温州 电信
社区乞丐
Credits -12
Posts 6
Joined 2009-04-02 05:53
17-year member
UID 141886
Gender Male
Status Offline
Learning
Forum Jump: