变量延迟,浅见认为就是变量预处理,在事先声明变量,告诉cmd环境哪一个先哪一个后。默认状况下是停用,能够用两种方法启用/停用:dom
1、cmd /v:on 和cmd /v:off ,范围在cmd这个环境直至exit 出现退出cmd函数
2、setlocal enabledelayedexpansion和setlocal disabledelayedexpansion范围在批处理文件范围内,直至endlocal出现停止.测试
先看看官方帮助set /?后以几个批处理代码注释解释。this
=================================================================================spa
考虑到读取一行文本时所遇到的目前扩充的限制时,延迟环境变量扩充是颇有用的,而不是执行的时候。如下例子说明直接变量扩充的问题:命令行
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "%VAR%" == "after" @echo If you see this, it worked
)code
不会显示消息,由于在读到第一个 IF 语句时,BOTH IF 语句中的 %VAR% 会被代替;缘由是: 它包含 IF 的文体,IF 是一个复合语句。因此,复合语句中的 IF 其实是在比较 "before" 和"after",这二者永远不会相等。一样,如下这个例子也不会达到预期效果:blog
set LIST=
for %i in (*) do set LIST=%LIST% %i
echo %LIST%token
缘由是,它不会在目前的目录中创建一个文件列表,而只是将LIST 变量设成找到的最后一个文件。这也是由于 %LIST% 在FOR 语句被读取时,只被扩充了一次;并且,那时的 LIST 变量是空的。所以,咱们真正执行的 FOR 循环是:作用域
for %i in (*) do set LIST= %i
这个循环继续将 LIST 设成找到的最后一个文件。延迟环境变量扩充容许您使用一个不一样的字符(惊叹号)在执行时间扩充环境变量。若是延迟的变量扩充被启用,能够将上面例子写成如下所示,以达到预期效果:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "!VAR!" == "after" @echo If you see this, it worked
)
set LIST=
for %i in (*) do set LIST=!LIST! %i
echo %LIST%
若是命令扩展名被启用,有几个动态环境变量能够被扩展,但不会出如今 SET 显示的变量列表中。每次变量数值被扩展时,这些变量数值都会被动态计算。若是用户用这些名称中任何一个定义变量,那个定义会替代下面描述的动态定义:
%CD% - 扩展到当前目录字符串。
%DATE% - 用跟 DATE 命令一样的格式扩展到当前日期。
%TIME% - 用跟 TIME 命令一样的格式扩展到当前时间。
%RANDOM% - 扩展到 0 和 32767 之间的任意十进制数字。
%ERRORLEVEL% - 扩展到当前 ERRORLEVEL 数值。
%CMDEXTVERSION% - 扩展到当前命令处理器扩展名版本号。
%CMDCMDLINE% - 扩展到调用命令处理器的原始命令行。
---------------------------------------------------------------------------------------------------------------------------------
开始批处理文件中环境改动的本地化操做。在执行 SETLOCAL 以后所作的环境改动只限于批处理文件。要还原原先的设置,必须执行 ENDLOCAL。达到批处理文件结尾时,对于该批处理文件的每一个还没有执行的 SETLOCAL 命令,都会有一个隐含的 ENDLOCAL 被执行。
SETLOCAL
若是命令扩展名被启用,SETLOCAL 会以下改变:
SETLOCAL 批命令如今能够接受可选参数:
ENABLEEXTENSIONS / DISABLEEXTENSIONS
启动或停用命令处理器扩展名。详细信息,请参阅 CMD /?。
ENABLEDELAYEDEXPANSION / DISABLEDELAYEDEXPANSION
启动或停用延缓环境变量扩展名。详细信息,请
参阅 SET /? 。
不管在 SETLOCAL 命令以前它们的设置是什么,这些修改会一直保留到匹配的 ENDLOCAL 命令。若是有一个参数,SETLOCAL 命令将设置 ERRORLEVEL 的值。若是有两个有效参数中的一个,该值则为零。用下列技巧,您能够在批脚本中使用这个来决定扩展名是否可用:
VERIFY OTHER 2>nul
SETLOCAL ENABLEEXTENSIONS
IF ERRORLEVEL 1 echo Unable to enable extensions
这个方法之因此有效,是由于在 CMD.EXE 的旧版本上,SETLOCAL不设置 ERRORLEVEL 值。具备不正确参数的 VERIFY 命令将ERRORLEVEL 值初始化成非零值。
=================================================================================
官方解释有些让人犯迷糊,如下几个代码注释部分来解释变量延迟,纰漏之处,请高手斧正.
=================================================================================
@echo off&setlocal enabledelayedexpansion
::第一方演示变量延迟,当输出if you ..延迟启动
::var重复赋值,第一个%var%取的是before
::第二个!var!取的是after,若不开启延迟变量
::不显示因!VAR!取的是before
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "!VAR!" == "after" @echo If you see this, it worked
)
pause>nul
-----------------------------------------------------------------------
@echo off&setlocal enabledelayedexpansion
:: 演示变量延迟,用setlocal disabledelayedexpansion
:: 和setlocal enabledelayedexpansion 相互切换
:: echo %var% 取set var=fds 的值,而echo !var!
:: 取for复合语句中set var=%%i的值;%var%的值与变量延迟
:: setlocal开关无关,但!var!的值与之相关。当变量延迟
:: 处于开状态则取%%i的赋值,反之则取!var!自己.
set var=fds
for /l %%i in (1,1,6) do (
set var=%%i
echo %var%
echo !var!
)
pause>nul&exit
----------------------------------------------------------------
@echo off&setlocal enabledelayedexpansion
::变量里套变量延迟演示
set a=40000
set b=df
set a%b%=70000
set c=!a%b%!
echo %c%
pause>nul
---------------------------------------------------------------
@echo off&setlocal enabledelayedexpansion
::显示2个随机数并截取各数字求和
mode con:cols=40 lines=20 1>nul
color a1
set %random%=%random%
for /f "delims=" %%i in ('set') do set num=%%i&call echo %%num%%&goto be
:be
for /f "tokens=1,2 delims==" %%j in ("%num%") do (
set/a a=%%j
set/a b=%%k
)
for /f %%l in ("%a%%b%") do (
set/a a=%%l
set/a b=%%l
set/a a0=%a:~0,1%
set/a a1=%a:~1,1% 2>nul&set/a a1+=!a0!
set/a a2=%a:~2,1% 2>nul&set/a a2+=!a1!
set/a a3=%a:~3,1% 2>nul&set/a a3+=!a2!
set/a a4=%a:~4,1% 2>nul&set/a a4+=!a3!
set/a b0=%b:~0,1%
set/a b1=%b:~1,1% 2>nul&set/a b1+=!b0!
set/a b2=%b:~2,1% 2>nul&set/a b2+=!b1!
set/a b3=%b:~3,1% 2>nul&set/a b3+=!b2!
set/a b4=%b:~4,1% 2>nul&set/a b4+=!b3!
)
set/a b4+=%a4%
echo 和为:%b4%
pause>nul
出处:http://hi.baidu.com/jadych/item/f6b022a5048b30268819d330
==============================================================================
如下内容为mq0036我的补充:
先来看个变量延迟的例子:
1 @echo off 2 3 setlocal 4 set aa=aaa:bbb 5 call :ck %aa% 6 echo return=%str% 7 call :ddd 8 goto :eof 9 10 11 12 :ck 13 setlocal EnableDelayedExpansion 14 if "a"=="a" (set str2=ErrorOperation) else ( 15 echo else 16 for /f "tokens=1,* delims=:" %%m in ("%~1") do ( 17 set str1=%%~m 18 if /I "!str1!"=="aaa" set str2=%%~n 19 ) 20 ) 21 endlocal & set str=%str2% 22 goto :eof 23 24 25 :ddd 26 echo %zzz% 27 set zzz=old 28 goto :eof
相信各位对上面的程序都能看懂,不过我这里还要写点废话解释解释,高手请跳过。
1.使用setlocal设置变量为局部变量,结束标志是endlocal,若是不是有endlocal则做用范围表示到当前文件结束.
2.变量延迟使用setlocal EnableDelayedExpansion和setlocal DisableDelayedExpansion,若是不使用setlocal DisableDelayedExpansion则做用范围表示到当前文件结束.
说明:DisableDelayedExpansion只是关闭变量延迟,也就是在这个语句后面的没法使用变量延迟的功能(后面还有讲解)
3.if语句后若是要执行多条语句须要把多条语句使用括号括起来,当使用else时,即便if后只有单条语句,也要括起来。
BAT语法认为括号中的语句就是一条语句。
程序说明:
说变量延迟就要说到局部变量,若是他们嵌套使用,各个变量的取值会怎么样?
第三行,设置局部变量
:ck子程序的功能,是利用延迟把aa变量中根据冒号(:)分割提取字符串,而且带回分割后的字符串
:ddd子程序则是为了验证程序前面的setlocal的嵌套的做用域
程序运行方式:把程序保存为bat文件,运行cmd并切换到程序所在目录,运行程序,若是修改了程序则关闭当前cmd窗口从新打开cmd。
验证一:
把第三行的setlocal去掉,而后屡次运行程序,能够看出第一次执行时,echo %zzz%输出的是ECHO is off.后面再次执行的都输出了old字符串。
程序运行方式:把程序保存为bat文件,运行cmd并切换到程序所在目录,输入程序文件名运行
程序运行方式:把程序保存为bat文件,运行cmd并切换到程序所在目录,运行程序,若是修改了程序则关闭当前cmd窗口从新打开cmd。
---------------------------------------------------------------
验证二:
使用endlocal & set str=%str2%是为了把局部变量返回主调函数中
屡次调用执行该文件,变量echo %zzz%中的值都是空
程序运行方式:把程序保存为bat文件,运行cmd并切换到程序所在目录,运行程序,若是修改了程序则关闭当前cmd窗口从新打开cmd。
---------------------------------------------------------------
验证三:
在BAT文件中的一部分是局部变量,一部分是全局变量。修改程序代码以下:
setlocal
echo %aa%
set aa=aaa:bbb
::endlocal
call :ddd
endlocal
goto :eof
:ddd
echo %zzz%
set zzz=old
goto :eof
经过改变endlocal的位置能够看到,在调用语句的后面,但在子程序的前面使用,则会起到局部变量的效果,当放到调用语句的前面则是全局变量的效果。这里测试的是在主程序中调用setlocal的功能,因此endlocal也必须在主程序中使用。
并且setlocal跟子程序有关,若是在子程序中使用setlocal,则在该子程序结束前的endlocal有效,若是不写endlocal则默认做用域到子程序结束,也就是在子程序中使用setlocal则做用域不会超出子程序。看下面的示例:
@echo off
echo %aa%
set aa=aaa:bbb
call :bbb
call :ddd
goto :eof
:bbb
setlocal
echo %zzz%
set zzz=old
::endlocal
echo %xxx%
set xxx=xxx
goto :eof
:ddd
echo %y%
set y=yyyy
运行以上程序,把:bbb子程序中的endlocal启用和禁用,能够查看变量%zzz%和%xxx%的值,
程序运行方式:把程序保存为bat文件,运行cmd并切换到程序所在目录,运行程序,若是修改了程序则关闭当前cmd窗口从新打开cmd。
---------------------------------------------------------------
验证四:
把if "a"=="a" (set str2=ErrorOperation)...... 这个语句中的括号拿掉,无论条件是否成立,"a"=="a"或"a"=="b",则都会执行else里的语句
程序运行方式:把程序保存为bat文件,运行cmd并切换到程序所在目录,运行程序,若是修改了程序则关闭当前cmd窗口从新打开cmd。
---------------------------------------------------------------
验证五:延迟的启用与禁用,修改ck子程序,以下::cksetlocal EnableDelayedExpansionif "a"=="b" (set str2=ErrorOperation) else (echo elsefor /f "tokens=1,* delims=:" %%m in ("%~1") do (set str1=%%~mif /I "!str1!"=="aaa" set str2=%%~n))::setlocal DisableDelayedExpansionfor /l %%i in (1,1,3) do (set num=%%iecho !num!)setlocal DisableDelayedExpansiongoto :eof把setlocal DisableDelayedExpansion放到for语句以前和以后的区别,你本身能够测试看看效果,从这里个程序能够看到启用和禁用的区别。程序运行方式:把程序保存为bat文件,运行cmd并切换到程序所在目录,运行程序,若是修改了程序则关闭当前cmd窗口从新打开cmd。---------------------------------------------------------------