一,什么是延遲環(huán)境變量擴展?
延遲變量全稱"延遲環(huán)境變量擴展",要理解這個東西,我們還得先理解一下什么叫變量擴展!
CMD在解釋我們的命令的時候,首先會讀取一條完整的命令,然后對其進(jìn)行一些命令格式的匹配操作,看你所輸入的
命令格式是不是符合它的要求.如果我們要在命令中引用一些變量,那么我們?nèi)绾巫孋MD在解釋我們的命令時,能識別
出這個變量呢?這時我們就可以在變量名字兩邊加一個%號,如%name%.當(dāng)CMD在讀取我們的整條命令進(jìn)行格式匹配的時
候,就會發(fā)現(xiàn)name這個字符兩邊加了%號,就不會把它當(dāng)作普通字符處理,而是會把它當(dāng)作一個變量處理,變量名叫name
然后CMD就會找到變量名對應(yīng)的值,用該值替換掉這個變量名字(name),(如果變量名不存在值,就返回空值).再將這
個替換好并且匹配的命令執(zhí)行!這個替換值的過程,就叫做變量擴展,說白了就是把變量的名字,用它的值給替換掉后
執(zhí)行!也就是批處理如何識別一個變量的過程.(注意:這里只是變量的擴展的意思,不是延遲環(huán)境變量擴展,要理解延
遲環(huán)境變量擴展,必須先理解什么是變量的擴展) 也就是批處理如何識別一個變量的過程.
例1,
@echo off
set var=test
echo %var%
pause
CMD在讀取到echo %var%這句命令后,就會進(jìn)行匹配操作,它馬上就發(fā)現(xiàn)var字符兩邊有%號,這時CMD就會把它當(dāng)作一
個變量處理,查看這個var變量名是不是有值,如果有就用該值把變量名var給替換掉,這里我們的var在上一條命令set
var=test中,給var賦值為test,所以CMD會用test把%var%這個變量名替換掉,替換后的結(jié)果就為echo test了.這些步
驟都是CMD進(jìn)行匹配操作的步驟,匹配完后,他再執(zhí)行echo test這條語句,這時我們的CMD中就會echo出一個test了.
什么是環(huán)境變量擴展知道了,那什么是延遲環(huán)境變量擴展呢?
在理解環(huán)境變量擴展時,我們知道CMD在解釋命令時,首先會把一條完整的命令進(jìn)行讀取,然后進(jìn)行匹配操作,匹配時
它會把命令里的變量用變量的值給替換掉,然后執(zhí)行這個替換好的命令.問題就出在"一條完整的命令",在BAT中,IF
FOR這樣的命令都可以加括號,將一些命令嵌套在里面執(zhí)行.這樣的話對于一條可以加擴號嵌其他命令的命令,他的完
整格式就是for %%i in (....)這樣一個整體.此時,如果我們?nèi)绻诶ㄌ柪锩媲度胍恍┰O(shè)置變量值的命令,就會出現(xiàn)
問題了!
例2,
@echo off
for /l %%i in (1,1,5) do (
set var=%%i
echo %var%
)
pause
執(zhí)行后會顯示5個空行的錯誤提示!為什么?根據(jù)我們上面說的知識來理解。
通過這兩個例子,大家應(yīng)該已經(jīng)理解,如果只有環(huán)境變量擴展這個過程的話,如果我們在可以嵌套命令的命令中執(zhí)行
賦值操作時,會讓我們的BAT出現(xiàn)給變量賦值的問題.那么這個時候"延遲環(huán)境變量擴展",這個概念就被提出來了。
在批處理中,我們可以用setloacl enabledelayedexpansion 這個命令來啟用"延遲環(huán)境變量擴展" ,在我們啟用
了"延遲環(huán)境變量擴展"后,當(dāng)CMD在解釋含有嵌套格式的命令時,他會把嵌套的命令一條一條的先執(zhí)行一次,然后再進(jìn)
行匹配操作,這樣我們的賦值操作就會完成.并且在"延遲環(huán)境變量擴展"啟用后,CMD會用!號來判斷這是不是一個變量
。如沒啟用前變量用%name%這樣的格式判斷,啟用后就用!name!這樣的格式判斷了,這個符號我們需要注意!
例3,
@echo off
setlocal enabledelayedexpansion
set var=1
for /l %%i in (1,1,5) do (
set /a var =%%i
echo !var!
)
pause
這樣大家應(yīng)該明白什么是延遲環(huán)境變量擴展了吧.再來一個例子
例4,
@echo off
set var=test echo %test%
pause
這條命令放在一行,表示他是一條完整的命令,不啟用"延遲環(huán)境變量擴展",就會出現(xiàn)上面的賦值錯誤!改成下這樣就
OK了:
@echo off
setlocal enabledelayedexpansion
set var=test echo !var!
pause
二,批處理變量延遲詳解
關(guān)于環(huán)境變量延遲擴展,使用set /?可以查看到部分說明,不過考慮到其粗劣的翻譯水平,建議在查看之前,首
先chcp 437切換為英文查看原英文說明。鑒于文中已說得十分詳盡,而且有數(shù)個代碼示例,應(yīng)該不難理解。在此僅
略作一些補充。
在許多可見的官方文檔中,均將使用一對百分號閉合環(huán)境變量以完成對其值的替換行為稱之為“擴展(expansion
)”,這其實是一個第一方的概念,是從命令解釋器的角度進(jìn)行稱謂的,而從我們使用者的角度來看,則可以將它
看作是引用(Reference)、調(diào)用(Call)或者獲?。℅et)。
而命令解釋器是擴展環(huán)境變量的行為大致如下:首先讀取命令行的一條完整語句,在進(jìn)行一些先期的預(yù)處理之后
,命令被解釋執(zhí)行之前,會對其中用百分號閉合的字符串進(jìn)行匹配,如果在環(huán)境空間中找到了與字符串相匹配的環(huán)
境變量,則用其值替換掉原字符串及百分號本身,如果未得到匹配,則用一個空串替換,這個過程就是環(huán)境變量的
“擴展”,它仍然屬于命令行的預(yù)處理范疇。
而一條“完整的語句”,在NT的命令解釋器CMD中被解釋為“for if else”等含有語句塊的語句和用“ | ||
”等連接起來的復(fù)合語句。
因此,當(dāng)CMD讀取for語句時,其后用一對圓擴號閉合的所有語句將一同讀取,并完成必要的預(yù)處理工作,這其中
就包括環(huán)境變量的擴展,所以在for中的所有語句執(zhí)行之前,所有的環(huán)境變量都已經(jīng)被替換為for之前所設(shè)定的值,
從而成為一個字符串常量,而不再是變量。無論在for中將那些環(huán)境變量如何修改,真正受到影響的只是環(huán)境變量空
間,而非for語句內(nèi)部。
而為了能夠在for語句內(nèi)部感知環(huán)境變量的動態(tài)變化,CMD設(shè)計了延遲的環(huán)境變量擴展特性,也就是說,當(dāng)CMD讀取
了一條完整的語句之后,它不會立即執(zhí)行變量的擴展行為,而會在某個單條語句執(zhí)行之前再進(jìn)行擴展,也就是說,
這個擴展行為被“延遲”了。
延遲環(huán)境變量擴展特性在CMD中缺省是關(guān)閉的,開啟它的方法目前有兩個:一是CMD /v:off(此處說法有誤,應(yīng)為
CMD /v:on——namejm 注),它會打開一個新的命令行外殼,在使用exit退出這個外殼之前,擴展特性始終有效,
常用于命令行環(huán)境中;二是setlocal EnableDelayedExpansion,它會使環(huán)境變量的修改限制到局部空間中,在
endlocal之后,擴展特性和之前對環(huán)境變量的修改將一同消失,常用于批處理語句中。
上面是willsort寫的帖子對于新手來說比較難理解。不過沒關(guān)系,我們先分析一個例子,同樣是引用willsort老
大的。
本例啟用了變量延遲,是個正確的例子!
例1,
@echo off setlocal EnableDelayedExpansion
for /f "tokens=* delims=" %%i in ("Hello world.") do (
set n=%%i
set n=!n:ld.=t!
set n=!n:o w= S!
set n=!n:He=Wi!
echo !n!
)
Pause
將上面代碼保存為.bat雙擊執(zhí)行后會顯示“Will Sort”字符串,下面將講解每個語句的意思:
1.@echo off setlocal EnableDelayedExpansion
關(guān)閉命令回顯,并啟用變量延遲
2.for /f "tokens=* delims=" %%i in ("Hello world.") do ( )
for命令及其參數(shù)的使用,請大家在論壇里搜索相關(guān)字眼。限于篇幅問題,這里不作討論。如果此時你不明白它的意
思,那么你就當(dāng)它的作用是把字符串“Hello world.”賦值給%%i好了,當(dāng)然這只是權(quán)宜之計,以后一定要學(xué)習(xí)for
的使用!
3.set n=%%i
把%%i的值(即Hello world.)賦予給變量n,這個大家都知道吧
4.set n=!n:ld.=t!
這里要講講set替換字符的功能了。這個語句的意思是,先獲取變量n的值(此時n的值是“Hello world.”),然后
將字符“t”替換字符“l(fā)d.”,然后再將替換后的結(jié)果再次賦值給變量n(此時n的值變?yōu)椤癏ello wort”)。至于
set替換字符的編寫格式,大家可以在CMD鍵入“set/?”找到“%PATH:str1=str2%”這段有說明
5.set n=!n:o w= S!
意思和上句一樣,只是替換和被替換的內(nèi)容不同。它是將“ S”替換“o w”(注意S前面和w前面都有個空格),其
實willsort老大是想證明set替換字符是支持句點和空格的(第4句“l(fā)d”后面有個.)。此時n的值為“Hell Sort”
6.set n=!n:He=Wi!
這句不用說了吧,執(zhí)行完這句后n的值為“Will Sort”
7.echo !n!
顯示變量n的值
需要注意的是,一旦啟用了變量延遲,就要用!號把變量括起來,而不能用%號。
好了,每句的意思已經(jīng)說完了,下面要講本帖真正要討論的變量延遲的問題。
這里又要引用Will Sort老大的說明:當(dāng)CMD讀取for語句時,其后用一對圓括號閉合的所有語句將一同讀取,并完成
必要的預(yù)處理工作,這其中就包括環(huán)境變量的擴展,所以在for中的所有語句執(zhí)行之前,所有的環(huán)境變量都已經(jīng)被替
換為for之前所設(shè)定的值,從而成為一個字符串常量,而不再是變量。
而為了能夠在for語句內(nèi)部感知環(huán)境變量的動態(tài)變化,CMD設(shè)計了延遲的環(huán)境變量擴展特性,也就是說,當(dāng)CMD讀取
了一條完整的語句之后,它不會立即執(zhí)行變量的擴展行為,而會在某個單條語句執(zhí)行之前再進(jìn)行擴展,也就是說,這
個擴展行為被“延遲”了。
總的來說是,在沒有啟用變量延遲的情況下,凡是在括號內(nèi)(即do里面)的變量,在執(zhí)行for語句之前,就已經(jīng)被
替換成for語句之前其它命令對該變量所賦予的值。這句話不懂沒關(guān)系,下面再看一個例子,看完你就會明白。
例2,
@echo off
for /f "tokens=* delims=" %%i in ("Hello world.") do (
set n=%%i
set n=%n:ld.=t%
set n=%n:o w= S%
set n=%n:He=Wi%
echo %n%
)
Pause
這和前面的例子差不多,只是所有!號都換成%號,這是個錯誤的例子。因為它沒有啟用變量延遲,也沒有使用!
號把變量括起來。我們看到它的執(zhí)行結(jié)果是顯示“ECHO 處于關(guān)閉狀態(tài)”。
為什么會這樣呢?原因是,在沒有啟用變量延遲的情況下,凡是在括號內(nèi)(即do里面)的變量,在執(zhí)行for語句之前
,就已經(jīng)被替換成for語句之前其它命令對該變量所賦予的值。
則是說在本例中的以下幾句
set n=%%i
set n=%n:ld.=t%
set n=%n:o w= S%
set n=%n:He=Wi%
echo %n%
第一句能正常執(zhí)行并達(dá)到它的目的,因為它只是單純地將%%i的值賦予給變量n,所以沒有任何問題。其它幾句屬
這樣情況:早在for語句執(zhí)行前,CMD就急不切待地將這幾句里面的所有變量n一同執(zhí)行替換行為,替換為for之前,
其它命令對n所設(shè)置的值,從而使n變成一個常量。但在本例中,for語句之前只有@echo off這句,并沒有其它命令
對n作過任何賦值行為,所以在for之前,變量n的值為空值。
即是說,set n=%n:ld.=t% 這句里面的變量n,在CMD讀取(注意是讀取不是執(zhí)行)完整個for語句后(這時還未輪
到set執(zhí)行自己的任務(wù)),就立刻被替換為一個空值,一個空值里面沒有任何東西,所以就不存在一字符替換另一字
符這種說法(沒有東西怎么替換?)。最終到執(zhí)行set n=%n:ld.=t%語句時,它只是獲取一個空值,再給變量n賦予
空值而已。其它幾句也是一樣原理。
所以,最后echo %n%的時候變量n還是個空值,而echo命令沒有東西可以顯示,就只有顯示“ECHO 處于關(guān)閉狀態(tài)
”這句來說明自己的狀態(tài)
通過這個例子的說明,相信大家已經(jīng)知道變量延遲的作用吧!我們再回頭來看看例1。
啟用變量延遲后,在執(zhí)行
set n=!n:ld.=t!
set n=!n:o w= S!
set n=!n:He=Wi!
echo !n!
這些語句前,它們里面的變量n不會馬上被CMD替換(啟用延遲后,CMD變得有耐性啦^_^),而未被替換的話,那么n
就還是變量,而不是常量。等到執(zhí)行set n=!n:ld.=t!等這幾句時,變量n才被替換。這樣每個set命令都能感知變量
n的任何變化,從而作出正確的替換行為。這就是變量延遲啦!
不要以為只有for才要用變量延遲,下面這個例子同樣需要
例3,這是個錯誤的例子
@echo off
set mm=girlecho %mm%
pause
執(zhí)行后依然顯示“ECHO 處于關(guān)閉狀態(tài)”。
原因是沒有啟用延遲,而且在set mm=girlecho %mm%語句前沒有其它命令對mm進(jìn)行賦值。這時當(dāng)CMD執(zhí)行set
mm=girlecho %mm%語句前,就已經(jīng)急不切待地把變量mm的值替換了,而又因為前面沒給mm賦值,所以mm被替換為空
值,變成常量。等到echo命令執(zhí)行時,它其實是echo一個不會變化的常量,本例中即是空值。
有人會問,echo前面不是給mm賦值了嗎?
這個就要關(guān)系到CMD解釋命令的步驟,大家可以參詳本帖開頭willsort的帖子。
總的來說是,如果不啟用變量延遲,在本例中,echo是不會理會也不會知道,它前面(指同一行語句)是否有其
它命令給mm賦值。它只會從set mm=girlecho %mm%這句以上的語句中獲取它所要顯示的變量的內(nèi)容,也就是說,上
一行或上幾行的命令將mm設(shè)置成什么值,echo命令就顯示什么值。
大家這樣做就明白了:
@echo off
set mm=boy
set mm=girlecho %mm%
pause
看看顯示什么結(jié)果就知道了!
這樣編寫例3才正確:
@echo offsetlocal EnableDelayedExpansion
set mm=girlecho !mm!
pause
開啟了變量延遲,變量擴展(替換)的行為就推遲到echo命令執(zhí)行時,這時echo能感知它前面的命令(本例的set)
對變量mm做了什么“壞事”,從而作出正確的判斷并執(zhí)行
三,批處理延遲變量(通俗解釋)
變量延遲 setlocal EnableDelayedExpansion
一個讓大多數(shù)新手頭痛的問題,網(wǎng)上教程雖多,但多半都是看不懂的,里面的專業(yè)術(shù)語太多。
以 cn-dos 聯(lián)盟的willsort的這篇教程為例,(個人認(rèn)為是解釋的極具權(quán)威和專業(yè)的)
但可能就是因為專業(yè),所以才看不懂,因為學(xué)cmd批處理的并不一定都是學(xué)計算機專業(yè)的。這個鬼東西確實不太好
理解,在下也是摸爬滾打多時,總結(jié)出一點點經(jīng)驗,現(xiàn)用通俗的方法解釋出來,希望能給新手些幫助,老鳥們見笑
了,若有不對的地方,歡迎指出。
言歸正傳
在什么時候需要延遲變量,和該如何引用延遲變量,我想這才是大多數(shù)新手迫切想要知道的問題。
耐心看完下面的內(nèi)容,我想對你應(yīng)該是有幫助的。
要想了解延遲變量,首先你要明白什么是“復(fù)合語句”好像又來了個“專業(yè)”名詞,別急,這個超好理解。所謂
“復(fù)合語句”就是指一對()里的所有命令。比如for的do后面
如:
for /f "delims=" %%i in (a.txt) do (
set var=%%i
echo %%i
set num=%%i
)
這里do后面的三句命令,在一對()里面,這就叫“復(fù)合語句”,當(dāng)然不止for 還有if 等等。。。
如:
if "%var%"=="abc" (
echo ok
set lis=123
)
反正就是凡是()里的所有命令,就叫“復(fù)合語句”
另外:這也是復(fù)合語句 set abc=123echo %abc% 沒錯,通過管道命令連接起來的命令,也是復(fù)合語句。
好,了解了復(fù)合語句,現(xiàn)在開始講延遲變量,也就說,在復(fù)合語句中才要使用延遲變量。
我們先不去理解什么叫“變量的擴展”這玩意叫法太專業(yè),我到現(xiàn)在都不太明白,
我們只要知道在什么時候需要使用延遲變量,如何才能正確提取到我們需要的變量就可以了,這才是我們的目的。
cmd在處理“復(fù)合語句”的時候,如果“復(fù)合語句”中用到了變量,會把變量的值當(dāng)作復(fù)合語句之前變量的值來引用
。如果在此之前變量沒有被賦值,就把它當(dāng)成空值。
例1,
@echo off
for /l %%i in (1 1 10) do (
set var=%%i
echo %var%
)
Pause
運行上面的代碼,顯示什么?顯示10個echo處于關(guān)閉狀態(tài)。按照邏輯,var的值應(yīng)該依次是 1、2、3........10 才
對啊!
這就是因為沒有開啟 延遲變量 的緣故,cmd把var的值當(dāng)作復(fù)合語句之前的值來引用,
而在本例中,復(fù)合語句之前并沒有給var定義,所以var的值是空的,所以會顯示10個echo處于關(guān)閉狀態(tài)。
例2,
@echo off
set var=abc
for /l %%i in (1 1 10) do (
set var=%%i
echo %var%
)
Pause
運行上面的代碼,會顯示什么,大家應(yīng)該知道了吧?
例3,
@echo off
set var=abc
for /l %%i in (1 1 5) do (
set var%%i=%%i
echo %var%
)
echo %var1% %var2% %var3% %var4% %var5%
pause
運行上面的代碼后,復(fù)合語句中所賦的值全部顯示出來了,這說明什么呢?
說明,在復(fù)合語句中,并不是沒有給變量賦值,只是你若沒有開啟延遲變量,你就沒法在復(fù)合語句中提取到它,要
等復(fù)合語句運行完畢后,才能提取到。
變量的表示方法:兩種: 1、%var% 2、!var!
第一種表示方法,大家都知道,第二種就是引用 延遲的變量。
在開啟了延遲變量的情況下,如果在復(fù)合語句之外,用哪種方法表示都可以。但是你若要在復(fù)合語句中引用復(fù)合語
句即時得到的變量,就要用第二種方法??蠢?/p>
例4,
@echo off
setlocal EnableDelayedExpansion
set var=abc
for /l %%i in (1 1 10) do (
set var=%%i
echo %var%
echo !var!
)
Pause
注意:例子中有兩個echo 一個是顯示 %var% 一個是顯示 !var!
結(jié)果很明白了,%var% 顯示的結(jié)果是復(fù)合語句之前變量var的值,而 !var! 顯示的就是復(fù)合語句中即時得到的值。
例5,
@echo off
setlocal EnableDelayedExpansion
for /l %%i in (1 1 5) do (
set var%%i=%%i
)
echo %var1% %var2% %var3% %var4% %var5%
echo !var1! !var2! !var3! !var4! !var5!
pause
這個例子說明什么,不用再解釋了吧?
說明在開啟了延遲變量的情況下,且在復(fù)合語句之外,用兩種方法都可以表示變量。就說到這吧。以上的解釋,完
全是出于個人的理解,也是為方便非專業(yè)人士理解,解釋肯定有錯誤的地方、就象學(xué)習(xí)英語時,為方便記憶,用漢
字的讀音來作解釋一樣。呵呵,是一種“偏門”各位新手千萬不要把以上所說的當(dāng)成是“真理”,否則就變成是“
誤人”了。
四,什么時候使用延遲變量?如何使用?
什么時候使用延遲變量?如何使用?這些一直是使新手困惑的地方,那到底是怎么樣的呢?那請看下面的例子,
我們將一步步引導(dǎo)大家。
例1,
@echo off
set /a num=0
for /l %%i in (1 1 3) do (
Rem ================================
set /a num =1
Rem 原意是變量num的值每次都加1
Rem ================================
echo %num%
)
pause>nul
先猜猜看,運行之后的結(jié)果是什么呢?你是不是認(rèn)為它會顯示:1 2 3 呢?我想大部分人會這么認(rèn)為。你再將以
上代碼保存為批處理文件,運行,看看結(jié)果。
你會看到,顯示的結(jié)果并不是意料中的 1 2 3 而是 0 0 0,這個是為什么呢?
原來這個是因為,批處理在處理for 或者if 語句中的變量時,先要進(jìn)行預(yù)處理,把其中的用%%括起來的變量,先
替換為語句之前的變量(如上面的代碼,for語句中的%num%早就被替換為語句前的 值:0),所以,for語句運行時
,雖然已經(jīng)給變量加了1,但是,值卻不變(因為echo %num%中的%num%早已被替換為:0了)。
那么,要實現(xiàn)(for或者if)語句中的變量實時的變化(如這里,我就要將1 2 3 顯示出來)要怎么辦呢?那就要起
用延遲變量,先在批處理中申明:setlocal enabledelayedexpansion 然后,將語句:echo %num%改成!num!(也就
是將“%”改成“!”),這樣就可以達(dá)到效果了,演示代碼:
例2,
@echo off
Rem ''''///////下面先申明起用延遲變量/////////////
setlocal enabledelayedexpansion
set /a num=0
for /l %%i in (1 1 3) do (
Rem ================================
set /a num =1
Rem 變量num的值每次都加1
Rem ================================
Rem '''''''//////////////////下面的變量不能再用"%"括起來,而應(yīng)該用"!"http:////////////
echo !num!
)
pause>nul
歸納總結(jié):
1、為什么要用延遲變量?
讓if語句和for語句中的變量實時變化;
2、什么時候用延遲變量?
一般是用在 for 語句和 if 語句中;
3、怎么用延遲變量?
先在批處理中申明起用延遲變量:setlocal enabledelayedexpansion
然后將 for 語句、if語句中的變量用兩個"!"括起來即可
4、其實在使用變量嵌套變量也可以使用變量延遲的。
例3,
@echo off
set a=1
set b1=10
echo %b%a%%
pause
執(zhí)行顯示,得到 %b1%
其實我想得到的是 賦予b1的值,即 10 那么如何實現(xiàn)呢?將上例修改如下,
例4,
@echo off
set a=1set b1=10
call,echo %%b%a%%%
pause>nul
call 這里實際是對命令行進(jìn)行重新組織擴展,先擴展%%b%a%%%里面的%a%,使%a%變成a的值1,再用cal來擴展%b1%
。
也可以用變量延遲來實現(xiàn),方法如下:
例5,
@echo off
set /a a=1,b1=10
Setlocal EnableDelayedExpansion
echo:!b%a%! ...
pause
call 在這里的用法實際是變量延遲的一種快捷方式,變量延遲一般用在for的循環(huán)體里面。
call,%%b%a%%% 這里的逗號實際是一個分隔符,和空格一樣,還有很多分隔符可用,比如上例中的 echo:!b%a%! ,
當(dāng)然并不是所有的命令都可以這樣用,看情況而定……
例6,
@echo offsetlocal enabledelayedexpansion
set a=1000
set b=dd
set a%b%=9000
set c=!a%b%!
echo %c%
pause
執(zhí)行一下,看看顯示的將是什么?為什么是這樣?相信通過例4,例5你也能分析得出來吧?
您可能感興趣的文章:- 批處理中setlocal enabledelayedexpansion的作用詳細(xì)整理
- cmd SETLOCAL使用介紹