变量延迟,浅见认为就是变量预处理,在事先声明变量,告诉cmd环境哪个先哪个后。默认情况下是停用,可以用两种方法启用/停用:

一、cmd /v:on 和cmd /v:off ,范围在cmd这个环境直至exit 出现退出cmd

二、setlocal enabledelayedexpansion和setlocal disabledelayedexpansion范围在批处理文件范围内,直至endlocal出现中止.

先看看官方帮助set /?后以几个批处理代码注释解释。

=================================================================================

考虑到读取一行文本时所遇到的目前扩充的限制时,延迟环境变量扩充是很有用的,而不是执行的时候。以下例子说明直接变量扩充的问题:

set VAR=before
    if "%VAR%" == "before" (
        set VAR=after
        if "%VAR%" == "after" @echo If you see this, it worked
    )

不会显示消息,因为在读到第一个 IF 语句时,BOTH IF 语句中的 %VAR% 会被代替;原因是: 它包含 IF 的文体,IF 是一个复合语句。所以,复合语句中的 IF 实际上是在比较 "before" 和"after",这两者永远不会相等。同样,以下这个例子也不会达到预期效果:

set LIST=
    for %i in (*) do set LIST=%LIST% %i
    echo %LIST%

原因是,它不会在目前的目录中建立一个文件列表,而只是将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个人补充:
先来看个变量延迟的例子:

 @echo off

 setlocal
set aa=aaa:bbb
call :ck %aa%
echo return=%str%
call :ddd
goto :eof :ck
setlocal EnableDelayedExpansion
if "a"=="a" (set str2=ErrorOperation) else (
echo else
for /f "tokens=1,* delims=:" %%m in ("%~1") do (
set str1=%%~m
if /I "!str1!"=="aaa" set str2=%%~n
)
)
endlocal & set str=%str2%
goto :eof :ddd
echo %zzz%
set zzz=old
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子程序,如下:
:ck
setlocal EnableDelayedExpansion
if "a"=="b" (set str2=ErrorOperation)  else (
echo else
for /f "tokens=1,* delims=:" %%m in ("%~1") do (
set str1=%%~m
if /I "!str1!"=="aaa" set str2=%%~n
)
)
::setlocal DisableDelayedExpansion
for /l %%i in (1,1,3) do (set num=%%i
echo !num!)
setlocal DisableDelayedExpansion
goto :eof
把setlocal DisableDelayedExpansion放到for语句之前和之后的区别,你自己可以测试看看效果,从这里个程序可以看到启用和禁用的区别。
程序运行方式:把程序保存为bat文件,运行cmd并切换到程序所在目录,运行程序,如果修改了程序则关闭当前cmd窗口重新打开cmd。
---------------------------------------------------------------

最新文章

  1. Centos7 关闭防火墙
  2. ActiveMQ笔记(1):编译、安装、示例代码
  3. C#-WebForm-WebForm开发基础
  4. python 二叉树
  5. (转)hbase master挂掉-zookeeper连接超时原因
  6. .NET初学者推荐课程 asp.net错误代码大全
  7. Android SDK安装教程
  8. SlickGrid example 3a: 可编辑单元
  9. JDK1.7新特性
  10. JavaScript的匿名函数和模块化的使用方法
  11. wsus客户端/服务器检查更新
  12. CrackMe_001
  13. 使用pdb调试python
  14. WinForm 控件不闪烁
  15. 【转】install intel wireless 3165 driver for ubuntu 14.04.3
  16. saltstack主机管理项目:编写插件基类-获取主机列表-提取yaml配置文件(四)
  17. [物理学与PDEs]第5章第3节 守恒定律, 应力张量
  18. HTML5为输入框添加语音输入功能
  19. windows平台下实现高可用性和可扩展性-ARR和HLB
  20. java 常用的类

热门文章

  1. python-day8-字典的内置方法
  2. 阿里云ECS安装最新版本Node.js
  3. es-hadoop saveToEsWithMeta
  4. CAS 服务端数据库认证
  5. 1.2 C++命名空间(namespace)
  6. 2018-北航-面向对象第三次OO作业分析与小结
  7. 快播CEO王欣:流量跌到零也要转型
  8. Swift3 中文手册.CHM版
  9. Python 字符串的相关操作
  10. JavaScript权威指南——跳转语句