變量延遲,淺見認爲就是變量預處理,在事先聲明變量,告訴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。---------------------------------------------------------------