zoukankan      html  css  js  c++  java
  • 变量延迟(setlocal)之浅见

    变量延迟,浅见认为就是变量预处理,在事先声明变量,告诉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个人补充:
    先来看个变量延迟的例子:

     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子程序,如下:
    :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。
    ---------------------------------------------------------------

  • 相关阅读:
    java中变量命名和引用变量的一个坑
    java(2)之前往对象村
    java的print,printf,println以及输入
    HTML学习开篇
    今后的学习路径
    offline .net3.5
    java
    两个经典的Oracle触发器示例(轉)
    listview1
    Delphi连接Oracle控件ODAC的安装及使用(轉載)
  • 原文地址:https://www.cnblogs.com/mq0036/p/3478108.html
Copyright © 2011-2022 走看看