整理延迟变量扩展介绍补充
引用变量的值在批处理中习惯叫变量扩展。
使用一对百分号括起变量名(如:%VAR%)的形式表示变量扩展。
延迟引用变量的值叫延迟变量扩展。
使用一对感叹号括起变量名(如:!VAR!)的形式表示延迟变量扩展。
是指变量扩展这个动作被推迟。
有了变量扩展。为什么又多出一个延迟变量扩展呢?
先看下面程序片段1:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
echo %VAR%
)
运行结果是什么?
结果是:before
而正确逻辑结果应是:after
为什么会发生这样的事呢?
原来命令解释程序(cmd)在执行复合语句之前做了预处理,即读入复合语
句时做了变量扩展。对于程序片段1,命令解释程序读入if复合语句,接着
做VAR变量扩展,由于此时set VAR=after语句还没有执行,VAR变量的值
未被改变仍然是before,因此,%VAR%扩展为before ,echo %VAR%语句
变为echo before 。然后,命令解释程序从if语句的开头开始执行,很明显
改变VAR变量的值对echo before语句没有影响(因%VAR%已提前扩展为
before),执行结果是before 。
扩展后的程序片段1:
set VAR=before
if " before " == "before" (
set VAR=after
echo before
)
再看一个程序片段2:(给当前目录文件列表的每行加上序号)
set count=0
for %%i in (*) do (
set /a count += 1
echo %count% %%i
)
运行结果:所有序号都是0
不是正确的逻辑结果。
原因与程序片段1一样。命令解释程序读入for复合语句,接着做count变
量扩展,由于此时set /a count += 1语句还没有执行,count变量的值未被改
变仍然是0,因此,%count%扩展为0 ,echo %count% %%i语句变为echo 0
%%i 。然后,命令解释程序从for语句的开头开始执行,很明显改变count
变量的值对echo 0 %%i语句没有影响(因%count%已提前扩展为0),执行
结果的序号是0 。
扩展后的程序片段2:
set count=0
for %%i in (*) do (
set /a count += 1批处理文件怎么做
echo 0 %%i
)
为了解决以上问题,命令解释程序增加了在运行时才进行变量扩展的功能
--"延迟变量扩展"。即引入延迟变量扩展的表示形式(使用一对感叹号括起
变量名,如:!VAR!),这里简称它为"延迟变量扩展表示式"。命令解释程序
在读入语句时,遇到"延迟变量扩展表示式",对表示式中的变量不做变量扩
展,而推迟到在执行时才做变量扩展。因此,在if、else、for和用"& | &&
||"等连接起来的复合语句中,如果在变量扩展前变量的值发生了变化,就
要将变量扩展改为延迟变量扩展,令变量扩展在执行时才进行,从而得到变
化了的变量值。
因此,要将以上程序片段中变量扩展改为延迟变量扩展,修改后的程序片
段:
set VAR=before
if "!VAR!" == "before" (
set VAR=after
echo !VAR!
)
set count=0
for %%i in (*) do (
set /a count += 1
echo !count! %%i
)
但是,默认情况下,"延迟变量扩展"功能是停用的。
启用这项功能,有几种方法:
1. 运行cmd /v: on 进入命令提示符窗口,然后输入命令。
2. 直接运行cmd /v: on /c "commands"
3. 在批处理文件里使用这项功能之前设置:setlocal enabledelayedexpansion
4. 设置注册表,让cmd默认情况下启用这项功能,可参考命令:cmd/?
总结以上要点,将程序片段2完善后,得到以下批处理程序—myDir.bat:
:
: 给当前目录文件列表的每行加上序号
@echo off
setlocal enabledelayedexpansion
set count=0
for %%i in (*) do (
set /a count += 1
echo !count! %%i
)
运行程序,结果正确。
如果不想为何时使用延迟变量扩展而烦恼,可以一开始就使用延迟变量扩展,
替代变量扩展。但是,迟变量扩展的自身嵌套或者和变量扩展相互嵌套使用,
也存在问题。
另外,对于"延迟变量扩展"问题,有另一巧妙的解决方法(叫call方法),将
call命令放在需要"延迟变量扩展"的语句前面,同时变量扩展要再用一对百分
号括起。这时call的作用相当于"延迟变量扩展"功能。其实call命令具有对
它后面命令做预处理和执行功能。
再看程序片段1:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
echo %VAR%
)
改进后:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
call echo %%VAR%%
)
运行结果是:after
为什么运行结果是:after 呢?
命令解释程序读入if复合语句,接着做预处理,即对%VAR%做变量扩展。
而对于%%VAR%%,是这样处理,当遇到连续二个百分号时,会去掉一个,
剩下一个只是字面意思而没有变量扩展功能意思,于是VAR也当作字面意
思。接着遇到后面连续二个百分号时,同样会去掉一个。预处理后,call命
令变为call echo %VAR%,然后,命令解释程序从if语句的开头开始执行,
执行到call命令时,call命令对echo %VAR%再做一次预处理,即对%VAR%
做变量扩展,由于之前VAR变量被赋予after,于是%VAR%扩展为after,接
着,执行echo after。所以,运行结果是:after。
总结:要注意复合语句中的变量扩展。如果在变量扩展前变量的值发生
了变化,就要将变量扩展改为具有"延迟变量扩展"功能的形式。
题外话:"延迟变量扩展"问题,是个害人的陷井,如马路上的沙井被拿去井
盖一样,行人一不小心就陷进去。让新手头大,让老手麻烦。这是cmd的设
计缺陷,将修补缺陷的工作留给用户(自行盖井盖)是极不负责任! use
willsort老大上面的帖子,对于新手来说比较难理解。不过没关系
我们先分析一个例子,同样是引用willsort老大的。本例启用了变量延迟,是个正确的例子!
例1:
@echo off & setlocal EnableDelayedExpansion
for /f  "tokens=* delims=" %%i in ("Hello world.") do (
set n=%%i
set n=!n:ld.=t!
set n=!n:o w= S!
set n=!n:He=Wi!
echo !n!
)
pause
将上面代码保存为.bat双击执行后会显示“Will Sort”字符串,下面将讲解每个语句的意思:
1.@echo off & setlocal EnableDelayedExpansion
关闭命令回显,并启用变量延迟
2.for /f  "tokens=* delims=" %%i in ("Hello world.") do (
for命令及其参数的使用,请大家在论坛里搜索相关字眼。限于篇幅问题,这里不作讨论。如果此时你不明白它的意思,那么你就当它的作用是把字符串“Hello world.”赋值给%%i好了,当然这只是权宜之计,以后一定要学习for的使用!
3.set n=%%i
把%%i的值(即Hello world.)赋予给变量n,这个大家都知道吧
4.set n=!n:ld.=t!
这里要讲讲set替换字符的功能了。这个语句的意思是,先获取变量n的值(此时n的值是“Hello world.”),然后将字符“t”替换字符“ld.”,然后再将替换后的结果再次赋值给变量n(此时n的值变为“Hel
lo wort”)。至于set替换字符的编写格式,大家可以在CMD键入“set/?”到“%PATH:str1=str2%”这段有说明
5.set n=!n:o w= S!
意思和上句一样,只是替换和被替换的内容不同。它是将“ S”替换“o w”(注意S前面和w前面都有个空格),其实willsort老大是想证明set替换字符是支持句点和空格的(第4句“ld”后面有个.)。此时n的值为“Hell Sort”
6.set n=!n:He=Wi!
这句不用说了吧,执行完这句后n的值为“Will Sort”
显示变量n的值
需要注意的是,一旦启用了变量延迟,就要用!号把变量括起来,而不能用%号。
好了,每句的意思已经说完了,下面要讲本帖真正要讨论的变量延迟的问题。
这里又要引用Will Sort老大的说明:当CMD读取for语句时,其后用一对圆括号闭合的所有语句将一同读取,并完成必要的预处理工作,这其中就包括环境变量的扩展,所以在for中的所有语句执行之前,所有的环境变量都已经被替换为for之前所设定的值,从而成为一个字符串常量,而不再是变量。
而为了能够在for语句内部感知环境变量的动态变化,CMD设计了延迟的环境变量扩展特性,也就是说,当CMD读取了一条完整的语句之后,它不会立即执行变量的扩展行为,而会在某个单条语句执行之前再进行扩展,也就是说,这个扩展行为被“延迟”了。
总的来说是,在没有启用变量延迟的情况下,凡是在括号内(即do里面)的变量,在执行for语句之前,就已经被替换成for语句之前其它命令对该变
量所赋予的值。这句话不懂没关系,下面再看一个例子,看完你就会明白。
例2:
@echo off
for /f  "tokens=* delims=" %%i in ("Hello world.") do (
set n=%%i
set n=%n:ld.=t%
set n=%n:o w= S%
set n=%n:He=Wi%
echo %n%
)
pause
这和前面的例子差不多,只是所有!号都换成%号,这是个错误的例子。因为它没有启用变量延迟,也没有使用!号把变量括起来。我们看到它的执行结果是显示“ECHO 处于关闭状态”。
为什么会这样呢?原因是,在没有启用变量延迟的情况下,凡是在括号内(即do里面)的变量,在执行for语句之前,就已经被替换成for语句之前其它命令对该变量所赋予的值。
则是说在本例中的以下几句
set n=%%i
set n=%n:ld.=t%
set n=%n:o w= S%
set n=%n:He=Wi%
echo %n%
第一句能正常执行并达到它的目的,因为它只是单纯地将%%i的值赋予给变量n,所以没有任何问题。其它几句属这样情况:早在for语句执行前,CMD就急不切待地将这几句里面的所有变量n一同执行替换行为,替换为for之前,其它命令对n所设置的值,从而使n变成一个常量。但在本例中,for语句之前只有@echo off这句,并没有其它命令对n作过任何赋值行为,所以在for之前,变量n的值为空值。即是说,set n=%n:ld.=t% 这句里面的变量n,在CMD读取(注意是读取不是执行)完整个for语句后(这时还未轮到set执行自己的任务),就立刻被替换为一个空值,一个空值里面没有任何东西,所以就不存在一字符替换另一字符这种说法(没有东西怎么替换?)。最终到执行set n=%n:ld.=t%语句时,它只是获取一个空值,再给变量n赋予空值而已。其它几句也是一样原理。
所以,最后echo %n%的时候变量n还是个空值,而echo命令没有东西可以显示,就只有显示“ECHO 处于关闭状态”这句来说明自己的状态
通过这个例子的说明,相信大家已经知道变量延迟的作用吧!我们再回头来看看例1。
启用变量延迟后,在执行
set n=!n:ld.=t!
set n=!n:o w= S!
set n=!n:He=Wi!
echo !n!
这些语句前,它们里面的变量n不会马上被CMD替换(启用延迟后,CMD变得有耐性啦^_^),而未被替换的话,那么n就还是变量,而不是常量。等到执行set n=!n:ld.=t!等这几句时,变量n才被替换。这样每个set命令都能感知变量n的任何变化,从而作出正确的替换行为。这就是变量延迟啦!
可跳过:
什么,说我讲得不好?没办法啊,因为偶太菜啊,只知道这些。偶只是淘两顿饭吃而已,望大家谅解啊,不要再拿砖头砸偶。。。不然偶就~~~~~~~~~~叫救命!^_^
这是正文不可跳过:
不要以为只有for才要用变量延迟,下面这个例子同样需要
例3:这是个错误的例子
@echo off
set mm
=girl&echo %mm%
pause
执行后依然显示“ECHO 处于关闭状态”。
原因是没有启用延迟,而且在set mm=girl&echo %mm%语句前没有其它命令对mm进行赋值。这时当CMD执行set mm=girl&echo %mm%语句前,就已经急不切待地把变量mm的值替换了,而又因为前面没给mm赋值,所以mm被替换为空值,变成常量。等到echo命令执行时,它其实是echo一个不会变化的常量,本例中即是空值。
有人会问,echo前面不是给mm赋值了吗?
这个就要关系到CMD解释命令的步骤,大家可以参详本帖开头willsort的帖子。
总的来说是,如果不启用变量延迟,在本例中,echo是不会理会也不会知道,它前面(指同一行语句)是否有其它命令给mm赋值。它只会从set mm=girl&echo %mm%这句以上的语句中获取它所要显示的变量的内容,也就是说,上一行或上几行的命令将mm设置成什么值,echo命令就显示什么值。
大家这样做就明白了:
@echo off
set mm=boy
set mm=girl&echo %mm%
pause
看看显示什么结果就知道了!
这样编写例3才正确:
@echo off&setlocal EnableDelayedExpansion
set mm=girl&echo !mm!
pause
开启了变量延迟,变量扩展(替换)的行为就推迟到echo命令执行时,这时echo能感知它前面的命令(本例的set)对变量mm做了什么“坏事”,从而作出正确的判断并执行
好了全篇完了,下课!
===========================================================================================
===========================================================================================
转自“简析环境变量和变量延迟特殊字符以及中介法的微妙关系”
简析环境变量和变量延迟特殊字符以及中介法的微妙关系
已作修改
本文主要以例子展开了讨论:
对每个例子的结果进行分析;并揭示其中的一些现象;
例一:
CODE:  [Copy to clipboard]
--------------------------------------------------------------------------------
@echo off
set "var=kljlk!tsd!21%mk%gd"
set var
结果为
Quote:
var=kljlk!tsd!21gd
(注意到 此时setlocal默认为disabledelayedexpansion !!不被识别)
因此把%mk%看做环境变量被替换掉;而mk没有被定义因此%mk%被替换为空
再来看加了setlocal enabledelayedexpansion后会是怎样;
例二:
CODE:  [Copy to clipboard]
--------------------------------------------------------------------------------
@echo off&setlocal enabledelayedexpansion
set "var=kljlk!tsd!21%mk%gd"
echo.!var!
结果为
Quote:
kljlk21gd
注意到 此时setlocal声明为enabledelayedexpansion
因此把!tsd!和%mk%均被看做环境变量而被替换掉;而tsd和mk没有被定义因此均被替换为空
(由于优先级的不同;%mk%将在预处理被先被替换,然后!tsd!被替换;原因后面有讲到)
如果把kljlk!tsd!21%mk%gd放到t.txt
然后
再通过%%a做为中介进行传递后会有什么现象呢
例三:
CODE:  [Copy to clipboard]
--------------------------------------------------------------------------------
@echo off
for /f "delims=" %%a in (t.txt) do (
set "var=%%a"
set var
)
结果为:
Quote:
var=kljlk!tsd!21%mk%gd
如果按照上面所说的此时%mk%应该被替换为空;
但是这里经过一个%%a的中介;此时的结果不会把%mk%替换;即使mk环境变量有定义也不会替换掉;而是按字面输出;
实际上这跟cmd的预处理机制有关;
当CMD读取for语句时,他的所有语句将一同被读取,并完成预处理工作,这其中就包括环境变量%%的扩展;
因为字符串在文本中 并没有以字符串的形式出现在 for语句中;因此其中的%%躲过了预处理机制;
但是当启用延迟环境变量后
例如:
例四
CODE:  [Copy to clipboard]
--------------------------------------------------------------------------------
@echo off
for /f "delims=" %%a in (t.txt) do (
set "var=%%a"
set var
)
结果为:
Quote:
var=kljlk21%mk%gd 
首先他会和上面一样对for所有语句一起读取;完成对%%的扩展;(%%的优先级高)
然后当CMD读取了一条完整的语句之后,
它不会立即执行变量的扩展行为,而会在某个单条语句执行之前再进行扩展,也就是说,这个扩展行为被“延迟”了
例如在set "var=%%a"时 只有执行到这条命令前才会对这条命令进行扩展;
而此时%%a已经被kljlk!tsd!21%mk%gd所代替;因此将会对其中的!!进行扩展.
他会识别其中是否存在!;此时的扩展只处理!!而不处理%%(因为对%%的处理过程在先前已经进行完;而那时因为字符串在文本中 并没有以字符串的形式出现在 for语句中;因此其中的%%躲过了预处理机制;);
然而只有当该字符串中含有!时, cmd才会对该字符串进行再次处理;
举例:
例五:
CODE:  [Copy to clipboard]
--------------------------------------------------------------------------------
@echo off&setlocal enabledelayedexpansion
for /f "delims=" %%a in (t.txt) do (
set "var=%%a"
set var
)
<的内容
Quote:
kl&>jl^k!tsd!21%mk%gd
kl&>jlk^!tsd^!21%mk%gd
dgssdgdg^gds^gdsa
结果为:
Quote:
var=kl&>jlk21%mk%gd
var=kl&>jlk!tsd!21%mk%gd
var=dgssdgdg^gds^gdsa
注意到 第一行的 ^ 已经不存在了;(所在行包含!!)
而第三行的^没有被处理;(所在行不包含!!)
(包含一个!也会对^进行处理;)
由此可见 启用延迟环境变量后 ;
在每次执行语句前;
cmd会检查是否含有!;
如果存在就对其进行必要的预处理后再执行.
注:此时setlocal默认为disabledelayedexpansion因此!tsd!是因为不给识别而不被替换和%mk%不被替换的原因不同.
如果想强迫进行二次转变(即把%mk%的值按其环

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。