变量延迟,浅见认为就是变量预处理,在事先声明变量,告诉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。
---------------------------------------------------------------

变量延迟(setlocal)之浅见的更多相关文章

  1. setlocal 与 变量延迟

    setlocal 与 变量延迟 本条内容引用[英雄出品]的批处理教程: 要想进阶,变量延迟是必过的一关!所以这一部分希望你能认真看. 为了更好的说明问题,我们先引入一个例子.例1: @echo off ...

  2. for批处理skip参数不支持变量延迟!n!的解决办法

    a.txt 文件a第1行 文件a第2行 文件a第3行 b.txt 文件b第1行 文件b第2行 文件b第3行 合并ab .bat @echo off REM 把两个文件逐行合并成一列 set n=0 f ...

  3. PHP eval变量延迟赋值

    $str = 'and {$prev}name like \'%五子棋%\'';$prev = "table.";eval("\$str = \"$str\&q ...

  4. BAT技巧

    FOR使用 枚举input.txt里的每一行,执行call :dosth %%i(以换行符为分割,默认是空格) for /f "delims=" %%i in (input.txt ...

  5. 【工作笔记】BAT批处理学习笔记与示例

    BAT批处理学习笔记 一.批注里定义:批处理文件是将一系列命令按一定的顺序集合为一个可执行的文本文件,其扩展名为BAT或者CMD,这些命令统称批处理命令. 二.常见的批处理指令: 命令清单: 1.RE ...

  6. 通过DOS、SHELL批处理命令加载Lib并编译和打包Java项目(或者运行项目)

    有些时候,需要通过DOS批处理来编译整个项目的JAVA文件:并且编译后还要对Class文件进行打包成jar文件...这还不是最烦的,最烦的是,编译和打包的时候需要依赖其他多个jar文件,困难就这么来了 ...

  7. bat programming is easy and powerful

    用linux的角度来思考windows,习惯了linux的shell后, 再来看windows的bat编程,就简单多了,简直就是理所当然 实际上windows的cmd命令行和linux的shell命令 ...

  8. 第一个 bat 文件

    要写一个批处理命令 转换场景数据 包括从文件里读入 每一行信息是一个要转换的场景名字 可以拼出路径 到指定路径 执行命令 http://blog.csdn.net/mfx1986/article/de ...

  9. 【转】BAT 延迟变量

    延迟环境变量在bat里是重中之重,虽然前面说过,熟练应用for才算会写批处理,但如果不懂延迟环境变量的话,那么你就只能写出简单的批处理,而for语句也不能发挥最大的作用. 延迟环境变量在cmd下默认是 ...

随机推荐

  1. Educational Codeforces Round 57题解

    A.Find Divisible 沙比题 显然l和2*l可以直接满足条件. 代码 #include<iostream> #include<cctype> #include< ...

  2. PHP函数总结 (六)

    <?php /** * 递归函数(自调用函数): * 在函数体内直接或间接的自己调用自己 * 通常有一个条件判断是否需要执行递归,并且在特定条件下终止函数的递归调用动作,把目前流程的主控制权交回 ...

  3. bzoj3944: Sum 杜教筛板子题

    板子题(卡常) 也可能是用map太慢了 /************************************************************** Problem: 3944 Us ...

  4. bzoj3262: 陌上花开 三维偏序cdq分治

    三维偏序裸题,cdq分治时,左侧的x一定比右侧x小,然后分别按y排序,对于左侧元素按y大小把z依次插入到树状数组里,其中维护每个左侧元素对右侧元素的贡献,在bit查询即可 /************* ...

  5. OAF日志使用总结

    本文的完成感谢葛严大神授权使用LogUtil类,其次感谢Tavor大神的EBS OAF开发日志(见: EBS OAF开发中日志(Logging) ). 日志的使用是一门极大的学问,若读者有兴趣,可以自 ...

  6. C++:线程(std::thread)

    1.创建一个线程 创建线程比较简单,使用std的thread实例化一个线程对象就创建完成了,示例: #include <iostream> #include <thread> ...

  7. H5技术干货

    H5技术干货 meta标签相关知识 H5页面窗口自动调整到设备宽度,并禁止用户缩放页面 <meta name="viewport" content="width=d ...

  8. 往SD卡中写文件的方法。

    package com.lixu.writetosd; import java.io.File; import java.io.FileOutputStream; import java.io.IOE ...

  9. pymysql 返回数据为字典形式(key:value--列:值)

    一.需求 在数据库的操作中,有时需要直接返回数据库表中的栏位名称+栏位值的key:value这种字典格式的方法. Python DB-API使用流程: 引入API模块. 获取与数据库的连接. 执行SQ ...

  10. Java——文件过滤器

    body, table{font-family: 微软雅黑} table{border-collapse: collapse; border: solid gray; border-width: 2p ...