【1】for命令簡介 先把for循環(huán)與for命令類比一下,這樣學習理解快。 for 循環(huán)語句,一般格式如下: 1 for (表達式1;表達式2;表達式3)2 {3 循環(huán)體;4 } 1. 表達式1 一般為初始狀態(tài)賦值表達式,給控制變量賦初值。 2. 表達式2 一般為關(guān)系表達式或邏輯表達式,為循環(huán)控制條件。 3. 表達式3 一般為每次執(zhí)行循環(huán)體后向控制變量重新賦值的表達式(給控制變量增量或減量)。 4. 語句:循環(huán)體,一般為復合語句(即可能需要執(zhí)行多條語句)。 舉個實例: 1 for (int i=0; i < 100; ++i)2 {3 cout << i << endl;4 } for 命令,一般格式如下: 在cmd窗口中使用格式: FOR %variable IN (set) DO command [command-parameters] 在批處理腳本中使用格式: FOR %%variable IN (set) DO command [command-parameters] 1. 在cmd窗口中使用,變量名必須用單%引用(即:%variable);在批處理腳本中使用,變量名必須用雙%引用(即:%%variable)。 2. for、in和do是for命令的三個關(guān)鍵字,缺一不可。 3. 關(guān)鍵字in之后,do之前的括號不能省略。 舉個實例:新建一個文本文件,命名為fordemo,修改文件類型為bat,用Notepad++打開編輯內(nèi)容為: 1 @echo off2 for %%i in (1 2 3 4 5) do @echo %%i3 pause>nul 執(zhí)行結(jié)果: 嗯哼,原來for命令就這么簡單?嗨,同學,不要浮躁,保持冷靜,更要理智。 下面,且看對上例語句的分析: 從命令組成結(jié)構(gòu)由左向右剖析,除過關(guān)鍵字,依次分別為: 1. 變量名為i,i太簡單,作為變量名不具備見名知意的特點。當我把i修改為item,如下內(nèi)容: 1 @echo off2 for %%item in (1 2 3 4 5) do @echo %%item3 pause>nul 雙擊執(zhí)行。不對呀!一扇而過?反正本人機器執(zhí)行的確是。為什么會這樣子的? 哦,原來如此:for命令的形式變量只能是26個字母中的任意一個。 代碼先復原。不會吧?難道只能有26種形式變量名?這也太狹隘了吧!區(qū)分大小寫不?驗證一下,再把for之后,in之前的變量名i換成I(即大寫字母I),如下內(nèi)容: 1 @echo off2 for %%I in (1 2 3 4 5) do @echo %%i3 pause>nul 雙擊執(zhí)行。我勒個去~ 執(zhí)行結(jié)果竟然這樣: 哦,原來如此:字母區(qū)分大小寫(即就是 %%a 和 %%A 是兩個不同的變量名)。 代碼復原。那要這樣的話,那我想修改為數(shù)字可否呢?盡管,我已經(jīng)知道批處理腳本中%0~%9是特殊的10個形式變量,修改后如下內(nèi)容: 1 @echo off2 for %%0 in (1 2 3 4 5) do @echo %%03 pause>nul4 for %%1 in (6 7 8 9 10) do @echo %%15 pause>nul 雙擊執(zhí)行。結(jié)果如下: 看來一切正常哈。但是,為了便于腳本的維護以及避免兩種形式變量同時使用時的隱患(一種比另一種只差個%,常在河邊走,哪有不濕鞋的?。?,建議慎重選擇。 哦,原來如此:for命令的變量盡量不要使用數(shù)字(即不要隨便使用%%0~%%9)。 2. 關(guān)于in與do之間括號中的內(nèi)容。其實,這個括號就相當于一個容器,里面存放了待遍歷的所有元素(一個或多個變量或其他類型)。經(jīng)網(wǎng)詢,每個元素之間,可以用空格、跳格、逗號、分號或等號分隔。 各種不同的分隔,驗證舉例如下: 新建一個文本文件,命名為forItemdemo,修改文件類型為bat,用Notepad++打開編輯內(nèi)容為: 1 @echo off 2 rem 空格 3 for %%A in (1 2 3) do @echo %%A 4 pause>nul 5 rem 跳格 6 for %%B in (4 5 6) do @echo %%B 7 pause>nul 8 rem 逗號 9 for %%C in (7,8,9) do @echo %%C10 pause>nul11 rem 分號12 for %%D in (10;11;12) do @echo %%D13 pause>nul14 rem 等號15 for %%D in (13=14=15) do @echo %%D16 pause>nul 執(zhí)行結(jié)果如下: do之后就是相當于for循環(huán)語句的循環(huán)體部分,在此不做解釋。 【2】for命令使用 (1)for /l for /l l是指loop,即循環(huán)的意思。for語句本身就是一種“循環(huán)”,為什么還要這個呢?且看下文分解。 for /l 語句的完整格式: for /l %variable in (start,step,end) do command [command-parameters] 說明: [1] start指起始值;step指步間距;end指終止值。 [2] start、step和end都只能取整數(shù)(正負皆可)。 [3] 步間距step的值不能為0。 [4] 當步間距step的值為正整數(shù)時,終止值end不能小于初始值start。 [5] 當步間距step的值為負整數(shù)時,終止值end不能大于初始值start。 具體含義:從start開始計數(shù),以step為步長,直至最接近end的那個整數(shù)值為止,這之間有多少個數(shù),do后的語句就執(zhí)行多少次。 for /l 的簡單應用示例如下: 例1:正常語法。新建文本文件,命名為for1.1,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 @echo off2 for /l %%i in (1,2,10) do echo %%i3 pause>nul4 for /l %%i in (-1,-2,-10) do echo %%i5 pause>nul 執(zhí)行結(jié)果: 例2:死循環(huán)。新建文本文件,命名為for1.2,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 for /l %%i in (1,0,1) do echo abc2 pause>nul 例3:無效語句。新建文本文件,命名為for1.3,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 for /l %%i in (2,1,1) do echo abc2 pause>nul 當大家明白了for /l 的具體功能之后,是否會想到了與它有異曲同工之妙的goto循環(huán)語句呢? 似乎,for /l 和 goto 循環(huán)語句可以相互替換? 一般而言,for /l語句可以換成goto循環(huán),但是,goto循環(huán)并不一定能被 for /l 語句替換掉。具體原因,請大家仔細想想。 只是就大家非常關(guān)心的一個問題提供一個簡潔的答案,那就是:什么時候該用for /l計數(shù)循環(huán),而什么時候又該用goto條件循環(huán)? 答案非常簡單: 當循環(huán)次數(shù)確定的時候,首選for /l語句,也可使用goto語句但不推薦; 當循環(huán)次數(shù)不確定的時候,用goto語句將是唯一的選擇,因為,這個時候需要用if之類的條件語句來判斷何時結(jié)束goto跳轉(zhuǎn)。 (2)for /d /d ,完整的含義是 /directory,即為了處理文件夾。完整語句應該是這樣的: for /d %variable in (元素集合) do command [command-parameters] [1] 有通配符。當“元素集合”中包含有通配符 ? 或 * 時,它會匹配文件夾。但是,相比 for /r 而言,這個時候的for /d,其作用就小得可憐了。(for /r 待看下文) 它僅能匹配當前目錄下的第一級文件夾,或是指定目錄位置上的一級文件夾,而不能匹配更深路徑下的子文件夾。 例如,新建文本文件,命名為for2.1,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 @echo off 2 for /d %%i in (f:\test*) do echo %%i3 pause>nul 這樣的語句,會匹配到形如:f:\test、f:\test1、f:\test2 等之類的文件夾,若不存在這樣的文件夾,將不會有任何回顯。 [2] 無通配符。當“元素集合”中不包含任何的通配符時,它的作用和 "for %%i in (元素集合) do 命令語句集合" 這樣的語句無任何區(qū)別。 因此,for /d 的角色就變得很微妙,總結(jié)一下: 當“元素集合”中包含通配符 ? 或 * 時,它的作用就是匹配文件夾,此時,它僅能匹配當前目錄下的第一級文件夾,或是指定目錄位置上的文件夾。 在層次深度上不及 for /r,但和 for /r 一樣的壞脾氣:不能匹配帶隱藏屬性的文件夾; 在靈活性上不及for /f 和dir的組合; 當“元素集合”中不包含任何通配符的時候,它完全是 "for %%i in (元素集合) do ……" 語句的翻版,但是又稍顯復雜。 for /d 的作用是如此有限,使用的次數(shù)是如此之少,以至于一度找不到它的用武之地,認為它食之無味,棄之可惜,完全是雞肋一塊。 當某年某月,我在cmd窗口里寫下了這樣的代碼: for /d %i in (test*) do @echo %i 我的本意是想查看在我的臨時目錄下,長年累月的測試工作到底創(chuàng)建了多少測試文件夾,以便隨后把 echo 換成 rd 刪除之。 這個時候,我突然發(fā)現(xiàn)這條代碼是如此的簡潔,是 for /r 或 for 和 dir /ad /b 的組合所無法替代的(echo換成 rd 就可以直接刪除掉這些測試目錄)。 簡潔的代碼給我?guī)淼南矏們H僅持續(xù)了短短10幾秒的時間,我便開始了迷惘——能用到for /d 的類似情形,貌似少之又少且乏善可陳啊! [3] for /r /d 是可以一起使用的(在for有限的4個參數(shù)中,據(jù)我所知只有/r /d可以一起使用)。 例如,新建文本文件,命名為for2.2,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 @echo off2 for /r /d %%i in (*) do echo %%i3 pause>nul 效果:顯示當前目錄下所有的文件夾(包括子文件夾);等價于 "dir /ad /s /b"。 for /r /d 其實是對 /d 參數(shù)的擴展,/d參數(shù)本身只能處理第一層文件夾,但是加上/r 參數(shù)后就可以處理所有的子文件夾; 但是,for /r /d依然不能處理隱藏屬性的文件夾。 這里給出使用for /r /d的一般條件: 3.1 要對文件夾進行操作(dir /ad /s /b可以顯示,但不能對文件夾進行操作); 3.2 不處理隱藏屬性的文件夾(說到底,還是for /f 和 dir 結(jié)合的命令更強大些)。 (3)for /r for /r 語句的完整格式: for /r [[drive:]path] %%variable in (set) do command [command-parameters] 按照幫助信息里翻譯得四不像的說法,for /r 的作用是“遞歸”,我們換一個通俗一點的,叫“遍歷文件夾”。 更詳細的解釋:像下面的語句中,如果“元素集合”中只是一個點號,那么,這條語句的作用就是:列舉“目錄”及其之下的所有子目錄,對這些文件夾都執(zhí)行“命令語句集合”中的命令語句。其作用與嵌套進 for /f 復合語句的 "dir /ad /b /s 路徑" 功能類似。如果省略了“目錄”,將在當前目錄下執(zhí)行前面描述的操作。 那么,再具體一點,for /r具體的語句完整格式為: for /r 目錄 %%i in (元素集合) do 命令語句集合 先來個代碼加深一下理解:新建文本文件,命名為for3.1,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 @echo off 2 for /r f:\test %%i in (.) do echo %%i 3 pause>nul 執(zhí)行結(jié)果如下所示: 效果就是顯示 f:\test 目錄及其之下所有子目錄的路徑。 說到這里,其效果其實與 dir /ad /b /s f:\test 類似,請看執(zhí)行dir命令的結(jié)果: 那么,若要論兩者的區(qū)別,可以歸納出以下3點: [1] for /r 列舉出來的路徑最后都帶有斜杠和點號(即每個路徑末尾的“\.”),而 dir 語句則沒有,這就會對獲取到的路徑進行進一步加工產(chǎn)生影響。 [2] for /r 不能列舉帶隱藏屬性的目錄,而 dir 語句則可以通過指定 /a 后面緊跟的參數(shù)來獲取帶指定屬性的目錄,更加靈活; [3] 若要對獲取到的路徑進行進一步處理,則需要把 dir 語句放入 for /f 語句中進行分析,寫成如下形式: 1 @echo off2 for /f %%i in ('dir /ad /b /s') do echo %%i3 pause>nul 由于 for /r 語句是邊列舉路徑邊進行處理,所以,在處理大量路徑的時候,過程前期不會感到有停頓;而 for /f 語句則需要等到 dir /ad /b /s 語句把所有路徑都列舉完之后,再讀入內(nèi)存進行處理,所以,在處理大量路徑的時候,前期會感到有明顯的停頓。 第[2]點差別很容易被大家忽視,導致用 for /r 列舉路徑的時候會造成內(nèi)容遺漏; 第[3]點則會讓大家有更直觀的感受,很容易感覺到兩者之間的差別。 反過來,要是“元素集合”不是點號呢?那又如何? 來看看這個代碼: 1 @echo off 2 for /r f:\test %%i in (a b c) do echo %%i3 pause 運行的結(jié)果是: 原來,它的含義是:列舉 f:\test 及其所有的子目錄,對所有的目錄路徑都分別添加a、b、c之后再顯示出來。 再來看一個代碼: 1 @echo off 2 for /r f:\test %%i in (*.txt) do echo %%i 3 pause>nul 運行結(jié)果是: 這段代碼的含義是:列舉 f:\test 及其所有子目錄下的txt文本文件(以.txt結(jié)尾的文件夾不會被列出來)。 我們再回過頭來歸納一下這個語句的作用: for /r 目錄 %%i in (元素集合) do 命令語句集合 上面語句的作用是: [1] 列舉“目錄”及該目錄路徑下所有子目錄,并把列舉出來的目錄路徑和元素集合中的每一個元素拼接成形如“目錄路徑\元素”格式的新字符串,然后,對每一條這樣的新字符串執(zhí)行“命令語句集合”中的每一條命令; 特別需要注意的是: 當“元素集合”帶以點號分隔的通配符 ? 或 * 的時候,把“元素集合”視為文件(不視為文件夾即目錄),整條語句的作用是匹配“目錄”所指文件夾及其所有子文件夾下匹配的文件; 若不以點號分隔,則把“元素集合”視為文件夾(不視為文件); [2] 當省略掉“目錄”時,則默認針對當前目錄進行; [3] 當元素集合中僅僅是一個點號的時候,將只列舉目錄路徑; (二)for /r 還是 dir /ad /b /s 列舉目錄時該如何選擇? 前面已經(jīng)說過,當列舉目錄時,for /r 和 dir /ad /b /s 的效果是非常類似的,這就產(chǎn)生了一個問題: 當我要獲取目錄路徑并進行進一步處理的時候,兩者之間,我該如何選擇? 這個問題,前面其實已經(jīng)有過一些討論,現(xiàn)在我們再來作詳細的分析。再來分析一下兩者各自的優(yōu)缺點: [1] for /r: 1)優(yōu)點: ?、?只通過1條語句就可以同時實現(xiàn)獲取目錄路徑和處理目錄路徑的操作; ?、?遍歷文件夾的時候,是邊列舉邊處理的,獲取到一條路徑就處理一條路徑,內(nèi)存占用小,處理大量路徑的時候不會產(chǎn)生停頓感。 2)缺點: ?、?不能獲取到帶隱藏屬性的目錄,會產(chǎn)生遺漏; ?、?不能獲取帶指定屬性的目錄。 [2] dir /ad /s: 1)優(yōu)點: ?、?能一次性獲取帶任意屬性的目錄,不會產(chǎn)生遺漏; ?、?能通過指定不同的參數(shù)獲取帶任意屬性的目錄,更具靈活性。 2)缺點: ?、?dir /ad /s 語句僅能獲取到目錄路徑,若要實現(xiàn)進一步的處理,還需要嵌入 for /f 語句中才能實現(xiàn),寫法不夠簡潔; ?、?嵌入 for /f 語句之后,需要寫成格式: for /f "delims=" %%i in ('dir /ad /b /s') do …… 受 for /f 語句運行機制的制約,需要先列舉完所有的路徑放入內(nèi)存之后,才能對每一條路徑進行進一步的處理,處理大量路徑時,內(nèi)存占用量偏大,并且在前期會產(chǎn)生明顯的停頓感,用戶體驗度不夠好; 綜合上述分析,可以做出如下選擇: 1、若僅僅是為了獲取某文件夾及其所有子文件夾的路徑的話,請選擇 dir /ad /b /s 語句; 2、若需要過濾帶隱藏屬性的文件夾的話,for /r 和 dir 語句都可以實現(xiàn),但 for /r 內(nèi)存占用小,處理速度快,是上上之選; 3、若需要獲取所有文件夾,則除了 dir /ad /b /s 外,別無選擇,因為 for /r 語句會遺漏帶隱藏屬性的文件夾; 在實際的使用中,我更喜歡使用 for /f 和 dir 的組合,因為它不會產(chǎn)生遺漏,并能給我?guī)砀`活的處理方式,唯一需要忍受的,就是它在處理大量路徑時前期的停頓感,以及在這背后稍微有點偏高的內(nèi)存占用;在我追求速度且可以忽略帶隱藏屬性的目錄的時候,我會換用 for /r 的方案,不過這樣的情形不多——有誰會愿意為了追求速度而容忍遺漏呢? 備注:關(guān)于紅色加粗的隱藏屬性問題,在win7系統(tǒng)下不復現(xiàn)。即遍歷時候,可以羅列到隱藏屬性的目錄。參考文獻是winXP環(huán)境,所以關(guān)于這點待再確認。 (4)for /f 網(wǎng)詢很多資料,大家都說了一句話:for /f 是個十分強大的家伙。 “家伙”不知道什么意思?聽說古時候,一天有一個秀才經(jīng)過一個村莊,遇見一個衣衫襤褸的小孩,穿著棉衣,戴著涼帽,那時估計人生活比較艱難,冬季為了避寒估計也就不考慮那么多。秀才傲慢譏諷的對小孩說:“穿棉衣,戴涼帽,不知春秋?!毙『⒖戳丝?,一個陌生的人,不慌不忙的回了一首:“東來的,西往的,不是東西?!保ㄗ⒁猓骸洞呵铩泛汀稏|西》可都是當時的著作名稱) 接下來,讓我們一起學習一下這個家伙。 如果說,for語句是批處理中最強大的語句的話,那么,for /f 就是精華中的精華(小說的筆法,牛人出場時候,總得渲染一下氣氛哈)。 for /f 的強大,和它擁有眾多的開關(guān)密切相關(guān)。因為開關(guān)眾多,所以用法復雜,本章將分成若干小節(jié),為大家逐一介紹強大的 for /f 語句。 (一) 為解析文本而生:for /f 的基本用法 所有的對象,無論是文件、窗體、還是控件,在所有的非機器語言看來,無外乎都是形如“c:\test.txt”、“CWnd”之類的文本信息;而所有的對象,具體如ini文件中的某條配置信息、注冊表中的某個鍵值、數(shù)據(jù)庫中的某條記錄等等……都只能先轉(zhuǎn)化為具有一定格式的文本信息,方可被代碼識別、操控??梢哉f,編程的很大一部分工作,都是在想方設(shè)法絞盡腦汁如何提取這些文本信息。 而提取文本信息,則是for /f的拿手好戲:讀取文件內(nèi)容;提取某幾行字符;截取某個字符片段;對提取到的內(nèi)容再切分、打亂、雜糅……只要你所能想到的花樣,for /f 都會想方設(shè)法幫你辦到,因為,for /f 就是被設(shè)計成專門用于解析文本的。 光說不練不行,先來看個例子: 新建一個文本文件test.txt,編輯內(nèi)容如下: 1 本文的目標是:不求最好,但求更好,做最實用的批處理分享。2 本文地址:http://www.cnblogs.com/Braveliu/p/5081087.html。3 這里是:順序選擇循環(huán)的博客,新手學習提升的福地。 新建文本文件,命名為for4.1.1,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 @echo off 2 for /f %%i in (test.txt) do echo %%i3 pause>nul 執(zhí)行結(jié)果: 這段代碼,主要是讓你樹立這樣一種意識、一種觀念:讀取文本文件的內(nèi)容,請使用 for /f 語句! 備注:改為“逐行分析文本文件的內(nèi)容”,因為讀取文本文件內(nèi)容的方法命令有很多,比如重定向輸入,又比如type/more/find/sort等命令。 深入考慮一下,for /f 語句是把整個test.txt一次性顯示出來的嗎? 在這段代碼中,雖然執(zhí)行結(jié)果是把test.txt中的所有內(nèi)容都顯示出來了,貌似 for /f 語句是把整個test.txt內(nèi)容一次性顯示到屏幕上,實際上并非如此。 無論for語句做何種變化,它的執(zhí)行過程仍然遵循基本的for流程:依次處理每個元素,直到所有的元素都被處理為止。只不過在for /f語句中,這里的元素是指文件中的每一行,也就是說,for /f 語句是以行為單位處理文本文件的,這是一條極為重要的規(guī)則。在此強調(diào)它的重要性,希望在接下來的學習過程中,你能時刻牢記這一原則,那么,很多問題將會潛意識的迎刃而解。以下是驗證這一說法的源代碼(在原代碼的基礎(chǔ)上添加了& pause>nul語句,這樣在執(zhí)行過程中,每個元素即每行處理完成時,會需要按一下鍵盤的任意鍵): 1 @echo off 2 for /f %%i in (test.txt) do echo %%i & pause>nul3 pause>nul (二) 切分字符串的利器:delims= 也許你對for4.1.1.bat文件這段代碼不屑一顧:不就是把test.txt的內(nèi)容顯示出來了么?好像用處不大啊。 好吧!說明你很有進取精神,咱繼續(xù)往下看, 還用如上test.txt的文本。新建文本文件,命名為for4.2.1,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 @echo off 2 for /f "delims=," %%i in (test.txt) do echo %%i3 pause>nul 執(zhí)行結(jié)果: 看到了嗎?你驚奇地發(fā)現(xiàn),每行第一個逗號之后的所有內(nèi)容都不見了(如果有不存在逗號的行,則保留原樣),也就說,你成功地提取到了每行第一個逗號之前的所有內(nèi)容!試想一下,這段代碼會有什么用呢? 如果別人給了你一個軟件清單,每行都是“英文軟件名(逗號)中文軟件名”的格式,而你卻只想保留英文名的時候,這段代碼將是多么有用??! 再假設(shè),有這么一個IP文件,第一列是數(shù)字格式的IP地址,第二列是具體的空間地址,列與列之間用逗號分隔,而你想提取其中數(shù)字格式的IP,呵呵~~ 我不說你也知道該怎么辦了吧? 舉一反三,觸類旁通。要是文本內(nèi)容不是以逗號分隔,而是想以其他符號分隔呢?OK,把“delims=,”的逗號換成相應的符號就可以了。 在這里,我們引入了一個新的開關(guān):"delims=,",它的含義是:以逗號作為被處理字符串的分隔符號。 在批處理中,指定分隔符號的方法是:添加一個形如 "delims=符號列表" 的開關(guān),這樣,被處理的每行字符串都會被符號列表中羅列出來的符號切分開來。 需要注意的是:如果沒有指定"delims=符號列表"這個開關(guān),那么,for /f 語句默認以空格鍵或跳格鍵作為分隔符號。 驗證一下“注意”。新建一個文本文件test2.txt,內(nèi)容(把test文件每行內(nèi)容中間的標點符號都改為空格或跳格)如下: 1 本文的目標是 不求最好 但求更好 做最實用的批處理分享。2 本文地址 http://www.cnblogs.com/Braveliu/p/5081087.html。3 這里是 順序選擇循環(huán)的博客 新手學習提升的福地。 新建文本文件,命名為for4.2.2,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 @echo off 2 for /f %%i in (test2.txt) do echo %%i3 pause>nul 執(zhí)行結(jié)果: 深入考慮一下,如果我要指定的符號不止一個,該怎么辦? 在上面的講解中,提到了指定分隔符號的方法:添加一個形如“delims=符號列表”的開關(guān)。不知道你注意到?jīng)]有,我的說法是“符號列表”而非“符號”,這是有很大區(qū)別的。因為,意味著你可以一次性指定多個分隔符號! 好勒~ 測試一下,還用test.txt文件的內(nèi)容。 新建文本文件,命名為for4.2.3,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 @echo off 2 for /f "delims=.," %%i in (test.txt) do echo %%i 3 pause>nul 執(zhí)行結(jié)果: 可見,第一個點號或第一個逗號之前的內(nèi)容都被提取出來了。 for4.2.3 批處理的執(zhí)行過程是:逐行讀取test.txt中的內(nèi)容,以點號和逗號切分每一行的內(nèi)容(不存在點號和逗號的行,則不再切分,為了描述的方便,我們把被點號或逗號切分的一個一個的字符串片段,稱之為節(jié))。然后,for /f 會提取第一節(jié)的內(nèi)容作為最終結(jié)果,顯示在屏幕上。需要注意的是,在這里,所有行的字符串被切分成了兩個以上的節(jié)。但是,for4.2.3的代碼只會提取第一個節(jié)字符串的內(nèi)容,因為 for /f 語句默認只提取第一節(jié)的字符串。那要想提取其他節(jié)呢?繼續(xù)往下看。 (三) 定點提取:tokens= 上一節(jié)在講解 delims= 的時候,強調(diào) for /f 默認只能提取到第一節(jié)的內(nèi)容?,F(xiàn)在我們來思考一個問題:如果我要提取的內(nèi)容不在第一節(jié)上,那怎么辦?這回,就該輪到 tokens= 出山了。 tokens= 后面一般跟的是數(shù)字,如 tokens=2,也可以跟多個,但是每個數(shù)字之間用逗號分隔,如 tokens=3,5,8,它們的含義分別是:提取第2節(jié)字符串;提取第3、第5和第8節(jié)字符串(注意,這里所說的“節(jié)”,是由 delims= 這一開關(guān)劃分的,它的內(nèi)容并不是一成不變的)。 下面來看一個例子。新建一個文本文件test3.txt,編輯內(nèi)容如下: 1 你若盛開,清風自來,如若不來,還有霧霾。 如果,我想提取“如若不來”這四個字,該如何寫代碼呢? 我們稍微觀察一下test3內(nèi)容就會發(fā)現(xiàn),如果以逗號作為切分符號,就正好可以把“如若不來”化為單獨的一個“節(jié)”,結(jié)合上一節(jié)的講解,我們知道,"delims=," 這個開關(guān)是不可缺少的,而要提取的內(nèi)容在以逗號切分的第3節(jié)上,那么,tokens= 后面的數(shù)字就應該是3了,最終的代碼如下: 新建文本文件,命名為for4.3.1,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 @echo off 2 for /f "delims=, tokens=3" %%i in (test3.txt) do echo %%i3 pause>nul 執(zhí)行結(jié)果: 如果我們現(xiàn)在要提取的不只是一個“節(jié)”,而是多個那又怎么辦呢?比如,要提取以逗號切分的第3節(jié)和第4節(jié)字符串,嘗試寫一個。 新建文本文件,命名為for4.3.2,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 @echo off 2 for /f "delims=, tokens=3,4" %%i in (test3.txt) do echo %%i3 pause>nul 執(zhí)行結(jié)果: 臥槽,運行批處理后發(fā)現(xiàn),執(zhí)行結(jié)果還只是顯示了第3節(jié)的內(nèi)容。 哦,看來,echo 后面的 %%i 只接收到了 tokens=3,4 中第一個數(shù)值3所代表的那個字符串,而第二個數(shù)值4所代表的字符串因為沒有變量來接收,所以就無法在執(zhí)行結(jié)果中顯示出來了。 那么,要如何接收 tokens= 后面多個數(shù)值所指代的內(nèi)容呢? 對,問題就在這里。for /f 語句對這種情況做以下規(guī)定: 如果 tokens= 后面指定了多個數(shù)字,如果形式變量為%%i。那么,第一個數(shù)字指代的內(nèi)容用第一個形式變量 %%i 來接收,第二個數(shù)字指代的內(nèi)容用第二個形式變量 %%j 來接收,第三個數(shù)字指代的內(nèi)容用第三個形式變量 %%k 來接收……第N個數(shù)字指代的內(nèi)容用第N個形式變量來接收。其中,形式變量遵循字母的排序,第N個形式變量具體是什么符號,由第一個形式變量來決定:如果第一個形式變量是%%i,那么,第二個形式變量就是%%j;如果第一個形式變量用的是%%x,那么,第二個形式變量就是%%y。 現(xiàn)在回頭再去看for4.3.2文件內(nèi)容,應該修改成啥樣子呢? 新建文本文件,命名為for4.3.3,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 @echo off 2 for /f "delims=, tokens=3,4" %%i in (test3.txt) do echo %%i %%j3 pause>nul 執(zhí)行結(jié)果: OK,大功告成。那如果有這樣一個要求:顯示test3中的內(nèi)容,但是逗號要替換成空格,如何編寫代碼? 結(jié)合上面所學的內(nèi)容,稍加思索,你可能很快就得出了答案: 新建文本文件,命名為for4.3.4,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 @echo off 2 for /f "delims=, tokens=1,2,3,4" %%i in (test3.txt) do echo %%i %%j %%k %%l3 pause>nul 執(zhí)行結(jié)果: 寫完之后,你可能意識到這樣一個問題: 假如,要提取的“節(jié)”數(shù)不是4,而是10,或者20,或者更多,難道我也得從1寫到10、20或者更多嗎?有沒有更簡潔的寫法呢? 答案肯定是有的,那就是:如果要提取的內(nèi)容是連續(xù)的多“節(jié)”的話,那么,連續(xù)的數(shù)字可以只寫最小值和最大值,中間用短橫連接起來即可,比如 tokens=1,2,3,4 可以簡寫為 tokens=1-4 。 還可以把這個表達式寫得更復雜一點:tokens=1,2-4 tokens=1-3,4……怎么方便就怎么寫吧! 另外,大家可能還會看到一種比較怪異的寫法: 還用如上test3文件。新建文本文件,命名為for4.3.5,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 @echo off 2 for /f "delims=, tokens=1,*" %%i in (test3.txt) do echo %%i %%j3 pause>nul 執(zhí)行結(jié)果:
我勒個去!第一個逗號不見了,取代它的是一個空格符號,其余部分保持不變。其中奧妙就在這個星號上面。 tokens=后面所接的星號具備這樣的功能:依切分字符,將字符串從左往右切分成緊跟在*之前最大數(shù)值所表示的節(jié)數(shù)之后,字符串的其余部分保持不變,且整體被*所表示的一個變量接收。 理論講解是比較枯燥的,特別是為了嚴密起見,還使用了很多限定性的修飾詞,導致句子很長,增加了理解的難度,我們還是結(jié)合for4.3.5來講解一下吧。 test3的內(nèi)容被切分,切分符號為逗號,當切分完第一節(jié)之后,切分動作不再繼續(xù)下去,因為 tokens=1,* 中,星號前面緊跟的是數(shù)字1;第一節(jié)字符串被切分完之后,其余部分字符串不做任何切分,整體作為第二節(jié)字符串,這樣,test3就被切分成了兩節(jié),分別被變量%%i和變量%%j接收。 以上幾種切分方式可以結(jié)合在一起使用。不知道下面這段代碼的含義你是否能看得懂,如果看不懂的話,那就運行一下代碼,然后反復揣摩,你一定會更加深刻地理解本節(jié)所講解的內(nèi)容的: 新建文本文件,命名為for4.3.6,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 @echo off 2 for /f "delims=, tokens=2-3,1,*" %%i in (test3.txt) do echo %%i %%j %%k %%l3 pause>nul 執(zhí)行結(jié)果:
(四) 跳過無關(guān)內(nèi)容,直奔主題:skip = n 很多時候,有用的信息并不是貫穿文本內(nèi)容的始終,而是位于第N行之后的行內(nèi),為了提高文本處理的效率,或者不受多余信息的干擾,for /f 允許你跳過這些無用的行,直接從第N+1行開始處理,這個時候,就需要使用參數(shù) skip = n,其中,n是一個正整數(shù),表示要跳過的行數(shù)。例如: 仍使用test文件。新建文本文件,命名為for4.4.1,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 @echo off 2 for /f "skip=2" %%i in (test.txt) do echo %%i 3 pause>nul 執(zhí)行結(jié)果: 這段代碼將跳過頭兩行內(nèi)容,從第3行起顯示test.txt中的信息。 (五) 忽略以指定字符打頭的行:eol= 在cmd窗口中敲入:for /?,“官方”的解釋如下: 1 for /f "eol=; tokens=2,3* delims=, " %i in (myfile.txt) do @echo %i %j %k 會分析 myfile.txt 中的每一行,忽略以分號打頭的那些行…… 第一條解釋狗屁不通,頗為費解:行注釋字符的結(jié)尾是什么意思?“(就一個)”怎么回事? 結(jié)合第二條解釋,才知道eol有忽略指定行的功能。 但是,這兩條解釋是互相矛盾的:到底是忽略以指定字符打頭的行,還是忽略以指定字符結(jié)尾的行? 實踐是檢驗真理的唯一標準,還是用代碼來檢驗一下eol的作用吧! 新建文本文件,命名為test4。編輯內(nèi)容如下: 1 ;愿得一人心,白首不相離。2 衣帶漸寬終不悔,為伊消得人憔悴。3 兩情若是久長時,又豈在朝朝暮暮。4 ;曾經(jīng)滄海難為水,除卻巫山不是云。5 落紅不是無情物,化作春泥更護花。 新建文本文件,命名為for4.5.1,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 @echo off 2 for /f "eol=;" %%i in (test4.txt) do echo %%i3 pause>nul 執(zhí)行結(jié)果: 觀察結(jié)果,總結(jié)規(guī)律發(fā)現(xiàn):那些以分號打頭的行沒有顯示出來。 由此可見,第二條解釋是正確的,eol= 的準確含義是:忽略以指定字符打頭的行。而第一條的“結(jié)尾”純屬微軟在信口開河。 那么,“(就一個)”又作何解釋呢?試試這個代碼: 新建文本文件,命名為for4.5.2,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 @echo off 2 for /f "eol=,;" %%i in (test4.txt) do echo %%i3 pause>nul 執(zhí)行結(jié)果: 屏幕上出現(xiàn) 此時不應有 ;"。 的報錯信息??梢?,在指定字符的時候,只能指定1個。 在很多時候,我對這樣的設(shè)計頗有微詞而又無可奈何:為什么只能指定1個而不是多個?要忽略多個還得又是if又是findstr加管道來多次過濾,那效率實在太低下了——能用到的功能基本上都提供,但是卻又做不到更好,批處理,你的功能為什么那么弱? 不知道大家注意到?jīng)]有,如果test4.txt中有以分號打頭的行,那么,這些行在代碼for4.4.1的執(zhí)行結(jié)果中將憑空消失。 重現(xiàn)一下作為驗證。新建文本文件,命名為test4,編輯內(nèi)容如下: 1 ;愿得一人心,白首不相離。2 衣帶漸寬終不悔,為伊消得人憔悴。3 兩情若是久長時,又豈在朝朝暮暮。4 ;曾經(jīng)滄海難為水,除卻巫山不是云。5 落紅不是無情物,化作春泥更護花。 新建文本文件,命名為for4.5.3,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 @echo off 2 for /f "skip=2" %%i in (test4.txt) do echo %%i 3 pause>nul 執(zhí)行結(jié)果: 觀察結(jié)果,分析總結(jié):原來,for /f 語句是默認忽略以分號打頭的行內(nèi)容的,正如它默認以空格鍵或跳格鍵作為字符串的切分字符一樣。 但是,備注:eol=; 這種默認設(shè)置,在delims=;時變得無效。再驗證一下這條: 新建文本文件,命名為test5,編輯內(nèi)容如下: 1 ;愿得一人心;白首不相離。 新建文本文件,命名為for4.5.4,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 @echo off 2 for /f "delims=;" %%i in (test5.txt) do echo %%i3 pause>nul 執(zhí)行結(jié)果: 很多時候,我們可以充分利用這個特點。比如,在設(shè)計即將用for讀取配置文件的時候,可以在注釋文字的行首加上分號。 如果要取消這個默認設(shè)置,可選擇的辦法是: 1、為eol=指定另外一個字符; 2、使用 for /f "eol=" 語句,也就是說,強制指定字符為空,就像對付 delims= 一樣。 (六)如何決定該使用 for /f 的哪種句式?(兼談usebackq的使用) for /f %%i in (……) do (……) for 語句有好幾種變形語句,不同之處在于第一個括號里的內(nèi)容:有的是用單引號括起來;有的是用雙引號包??;有的不用任何符號包裹。具體格式為: [1] for /f %%i in (文件名) do (……) [2] for /f %%i in ('命令語句') do (……) [3] for /f %%i in ("字符串") do (……) 看到這里,我想很多人可能已經(jīng)開始犯了迷糊了:如果要解決一個具體問題,面對這么多的選擇,如何決定該使用哪一條呢? 實際上,當我在上面羅列這些語句的時候,已經(jīng)有所提示了,不知道你是否注意到了。 如果你一時無法參透其中奧妙,那也無妨,請聽我一一道來便是。 ?。?)當你希望讀取文本文件中的內(nèi)容的話,第一個括號中不用任何符號包裹,應該使用的是第[1]條語句。 例如:你想顯示test.txt中的內(nèi)容,那么,就使用 1 @echo off 2 for /f %%i in (test.txt) do echo %%i3 pause>nul ?。?)當你讀取的是命令語句執(zhí)行結(jié)果中的內(nèi)容的話,第一個括號中的命令語句必須使用單引號包裹,應該使用的是第[2]條語句。 例如:你想顯示當前目錄下所有文件名中含有test字符串的文本文件的時候,應該使用 1 @echo off2 for /f %%i in ('dir /a-d /b *test*.txt') do echo %%i3 pause>nul ?。?)當你要處理的是一個字符串的時候,第一個括號中的內(nèi)容必須用雙引號括起來,應該是用的是第[3]條語句。 例如:當你想把www.baidu.cn這串字符中的點號換為短橫線并顯示出來的話,可以使用 1 @echo off2 for /f "delims=. tokens=1-3" %%i in ("www.baidu.cn") do echo %%i-%%j-%%k3 pause>nul 很顯然,第一個括號里是否需要用符號包裹起來,以及使用什么樣的符號包裹,取決于要處理的對象屬于什么類型:
當然,事情并不是絕對如此,如果細心的你想到了批處理中難纏的特殊字符,你肯定會頭大如斗。 或許你頭腦中靈光一閃,已經(jīng)想到了一個十分頭痛的問題:在第1條語句中,如果文件名中含有空格 或 &,該怎么辦? 照舊嗎?嘗試一下: 新建文本文件,命名為test 6,編輯內(nèi)容如下: 1 abcdefghijklmnopqrstuvwxyz. 新建文本文件,命名為for4.6.1,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 @echo off 2 for /f %%i in (test 6.txt) do echo %%i3 pause>nul 執(zhí)行結(jié)果: 當你興沖沖地雙擊批處理,運行后,屏幕上出現(xiàn)了可恥的報錯信息:系統(tǒng)找不到文件 test 。 當你把 test 6.txt 換成 test&6.txt 后,更怪異的事情發(fā)生了:CMD窗口在你眼前一閃而過,然后,優(yōu)雅地消失了。 你可能覺得自己的代碼寫錯了某些符號,你再仔細的檢查了一次,確認沒有筆誤,然后,你再次雙擊批處理,結(jié)果問題照舊;你開始懷疑其他程序?qū)λ赡苡杏绊懀谑顷P(guān)掉其他窗口,再運行了一次,問題依舊;你不服氣地連續(xù)運行了好幾次,還是同樣的結(jié)果。 真怪哉! 你一拍大腿,猛然想起了一件事:當路徑中含有特殊字符的時候,應該使用引號把路徑括起來。對,就是它了! 但是,當你把代碼寫出來之后,你很快就焉了: 1 for /f %%i in ("test 6.txt") do echo %%i 這不就是上面提到的第[3]條 for /f 命令的格式嗎?批處理會把 test 6.txt 這個文件名識別字符串??! 你百無聊賴地在CMD窗口中輸入 for /? ,并重重地敲下了回車,漫無目的地在幫助信息中尋找,希望能找到點什么。 結(jié)果還真讓你到了點什么。 你看到了這樣的描述: usebackq - 指定新語法已在下類情況中使用:在作為命令執(zhí)行一個后引號的字符串并且一個單引號字符為文字字符串命令并允許在 filenameset中使用雙引號擴起文件名稱。 但是,通讀一遍之后,你卻如墜五里霧中,不知所云。 還好,下面有個例子,并配有簡單的說明: 1 ::枚舉當前環(huán)境中的環(huán)境變量名稱2 @echo off3 for /f "usebackq delims==" %%i IN (`set`) do @echo %%i 4 pause>nul 你仔細對比了for /f語句使用usebackq 和 不使用usebackq時在寫法上的差別,很快就找到了答案:當使用了usebackq之后,如果第一個括號中是一條命令語句,那么,就要把單引號'改成后引號`(鍵盤左上角esc鍵下面的那個按鍵,與~在同一鍵位上)。回過頭去再看那段關(guān)于usebackq的描述,字斟句酌,反復揣摩,終于被你破譯了天機:usebackq 是一個增強型參數(shù),當使用了這個參數(shù)之后,原來的for語句中第一個括號內(nèi)的寫法要做如下變動:
驗證一下,把for4.6.1文件改寫成如下代碼: 1 @echo off2 for /f "usebackq" %%i in ("test 6.txt") do echo %%i3 pause>nul 執(zhí)行結(jié)果: 測試通過!此時,你很可能會仰天長嘆:Shit,微軟這該死的機器翻譯! 至于把代碼中的空格換成&后,CMD窗口會直接退出,那是因為&是復合語句的連接符,CMD在預處理的時候,會優(yōu)先把&前后兩部分作為兩條語句來解析,而不是大家想象中的一條完整的for語句,從而產(chǎn)生了嚴重的語法錯誤。因為牽涉到預處理機制問題,不屬于本節(jié)要討論的內(nèi)容,在此不做詳細講解。 這個時候,我們會吃驚地發(fā)現(xiàn),區(qū)區(qū)一條for語句,竟然有多達6種句型: 1 rem (1) 2 for /f %%i in (文件名) do (……) 3 rem (2) 4 for /f %%i in ('命令語句') do (……) 5 rem (3) 6 for /f %%i in ("字符串") do (……) 7 rem (4) 8 for /f "usebackq" %%i in ("文件名") do (……) 9 rem (5)10 for /f "usebackq" %%i in (`命令語句`) do (……) 11 rem (6)12 for /f "usebackq" %%i in ('字符串') do (……) 其中,4、5、6由1、2、3發(fā)展而來,他們有這樣的對應關(guān)系:1-->4、2-->5、3-->6。 好在后3種情形并不常用,所以,牢牢掌握好前三種句型的適用情形就可以了。否則,要在這么多句型中確定選擇哪一條語句來使用,還真有點讓人頭腦發(fā)懵。 至于 for /f 為什么要增加usebacq參數(shù),我只為第4條語句找到了合理的解釋:為了兼容文件名中所帶的空格 或 &。它在第5、6條語句中為什么還有存在的必要,我也不是很明白,這有待于各位去慢慢發(fā)現(xiàn)。(備注:這種解釋雖然有點不靠譜,但也算一種解釋,大家將就看看吧。啟用usebackq選項的時候,“文件名”取代了“字符串”,那么“字符串”只好改變?yōu)椤懊钫Z句”,“命令語句”只好用后引號重新表示——簡而言之,是“文件名”符號改變引起的蝴蝶效應。言外之意:usebackq 除了在處理帶空格的文件名時會用到外,根本就沒有其它的出場機會和存在價值。) (5)延遲變量 關(guān)于延遲變量變量延遲在for語句中起著至關(guān)重要的作用,不只是在for語句中,在其他的復合語句中,它也在幕后默默地工作著,為了突出它的重要性,本節(jié)內(nèi)容在單獨的樓層中發(fā)出來,希望引起大家的重視。 對于批處理新手而言,“變量延遲”這個概念很可能聞所未聞,但是,它卻像一堵橫亙在你前進道路上的無形高墻,你感受不到它的存在,但當你試圖往前沖時,它會把你狠狠地彈回來,讓你無法逾越、無功而返;而一旦找到了越過它的方法,你就會發(fā)現(xiàn),在for的世界里,前面已經(jīng)是一片坦途,而你對批處理的理解,又上升到了一個新的境界。 例如,你編寫了這樣一個代碼: 新建文本文件,命名為for5.1,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 @echo off 2 set num = 0 && echo %num% 3 pause>nul 執(zhí)行結(jié)果: 你的本意是想對變量num賦值之后,再把這個值顯示出來,結(jié)果,顯示出來的并不是0,而是顯示:ECHO 處于關(guān)閉狀態(tài)。 之所以會出錯,是因為“變量延遲”這個家伙在作怪。在講解變量延遲之前,我們需要了解一下批處理的執(zhí)行過程,它將有助于我們深入理解變量延遲。 批處理的執(zhí)行過程是怎樣的呢? “自上而下,逐條執(zhí)行”,我想,這個經(jīng)典的說法大家都已經(jīng)耳熟能詳了,沒事的時候倒著念,也還別有一番意味。但是,我想問大家的是,大家真的深刻地理解了這句話的含義了嗎?“自上而下”,這一條和我們本節(jié)的講解關(guān)系不大,暫時略過不說,后一條,“逐條執(zhí)行”和變量延遲有著莫大的干系,它是我們本節(jié)要關(guān)注的重點。很多人往往認為一行代碼就是一條語句,從而把“逐條執(zhí)行”與“逐行執(zhí)行”等同起來,這就大錯特錯了。莫非“逐條執(zhí)行”里暗藏著玄機? 正是如此。 “逐條”并不等同于“逐行”。這個“條”,是“一條完整的語句”的意思,并不是指“一行代碼”。在批處理中,是不是一條完整的語句,并不是以行來論的,而是要看它的作用范圍。什么樣的語句才算“一條完整的語句”呢? [1] 在復合語句中,整個復合語句是一條完整的語句,而無論這個復合語句占用了多少行的位置。常見的復合語句有:for語句、if……else語句、用連接符&、||和&&連接的語句,用管道符號|連接的語句,以及用括號括起來的、由多條語句組合而成的語句塊; [2] 在非復合語句中,如果該語句占據(jù)了一行的位置,則該行代碼為一條完整的語句。 例如:新建文本文件,命名為for5.2,修改文件類型為bat,用Notepad++打開編輯內(nèi)容如下: 1 @echo off 2 set num=0 3 for /f %%i in ('dir /a-d /b *.exe') do ( 4 set /a num+=1 5 echo num 當前的值是 %num% 6 ) 7 echo 當前目錄下共有 %num% 個exe文件 8 dir /a-d /b *.txt|findstr "test">nul&&( 9 echo 存在含有 test 字符串的文本本件10 ) || echo 不存在含有 test 字符串的文本文件11 if exist test.ini (12 echo 存在 test.ini 文件13 ) else echo 不存在 test.ini 文件14 pause 上面的代碼共有14行,但是只有完整的語句只有7條,它們分別是: 第1條:第1行的echo語句; 第2條:第2行的set語句; 第3條:第3、4、5、6行上的for復合語句; 第4條:第7行的echo語句; 第5條:第8、9、10行上用&&和||連接的復合語句; 第6條:第11、12、13行上的if……else復合語句; 第7條:第14行上的pause語句。 在這里,我之所以要花這么長的篇幅來說明一行代碼并不見得就是一條語句,是因為批處理的執(zhí)行特點是“逐條”執(zhí)行而不是“逐行”執(zhí)行,澄清了這個誤解,將會更加理解批處理的預處理機制。 在代碼“逐條”執(zhí)行的過程中,cmd.exe這個批處理解釋器會對每條語句做一些預處理工作,這就是批處理中大名鼎鼎的“預處理機制”。 預處理的大致情形是這樣的:首先,把一條完整的語句讀入內(nèi)存中(不管這條語句有多少行,它們都會被一起讀入),然后,識別出哪些部分是命令關(guān)鍵字,哪些是開關(guān)、哪些是參數(shù),哪些是變量引用……如果代碼語法有誤,則給出錯誤提示或退出批處理環(huán)境;如果順利通過,接下來,就把該條語句中所有被引用的變量及變量兩邊的百分號對,用這條語句被讀入內(nèi)存之就已經(jīng)賦予該變量的具體值來替換……當所有的預處理工作完成之后,批處理才會執(zhí)行每條完整語句內(nèi)部每個命令的原有功能。也就是說,如果命令語句中含有變量引用(變量及緊鄰它左右的百分號對),并且某個變量的值在命令的執(zhí)行過程中被改變了,即使該條語句內(nèi)部的其他地方也用到了這個變量,也不會用最新的值去替換它們,因為某條語句在被預處理的時候,所有的變量引用都已經(jīng)被替換成字符串常量了,變量值在復合語句內(nèi)部被改變,不會影響到語句內(nèi)部的其他任何地方。 順便說一下,運行代碼 for5.2 之后,將在屏幕上顯示當前目錄下有多少個exe文件,是否存在含有 test 字符串的文本文件,以及是否存在 test.ini 這個文件等信息。 讓很多人百思不得其解的是:如果當前目錄下存在exe文件,那么,有多少個exe文件,屏幕上就會提示多少次 "num 當前的值是 0" ,而不是顯示1到N(N是exe文件的個數(shù))。 結(jié)合上面兩個例子,我們再來分析一下,為什么這兩段代碼的執(zhí)行結(jié)果和我們的期望有一些差距。 在 for5.1 中,set num=0 && echo %num% 是一條復合語句,它的含義是:把0賦予變量num,成功后,顯示變量num的值。 雖然是在變量num被賦值成功后才顯示變量num的值,但是,因為這是一條復合語句,在預處理的時候,&&后的%num%只能被set語句之前的語句賦予變量num的具體值來替換,而不能被復合語句內(nèi)部、&&之前的set語句對num所賦予的值來替換,可見,此num非彼num??墒牵谶@條復合語句之前,我們并沒有對變量num賦值,所以,&&之后的%num%是空值,相當于在&&之后只執(zhí)行了 echo 這一命令,所以,會顯示 echo 命令的當前狀態(tài),而不是顯示變量num的值(雖然該變量的值被set語句改變了)。 在 for5.2 中,for語句的含義是:列舉當前目錄下的exe文件,每發(fā)現(xiàn)一個exe文件,變量num的值就累加1,并顯示變量num的值。 看了對 for5.1 的分析之后,再來分析 for5.2 就不再那么困難了: 第3、4、5行上的代碼共同構(gòu)成了一條完整的for語句,而語句"echo num 當前的值是 %num%"與"set /a num+=1"同處復合語句for的內(nèi)部,那么,第4行上set改變了num的值之后,并不能對第5行上的變量num有任何影響,因為在預處理階段,第5行上的變量引用%num%已經(jīng)被在for之前就賦予變量num的具體值替換掉了,它被替換成了0(是被第2行上的set語句賦予的)。 如果想讓代碼for5.1 的執(zhí)行結(jié)果中顯示&&之前賦予num的值,讓代碼 for5.2 在列舉exe文件的時候,從1到N地顯示exe文件的數(shù)量,那又該怎么辦呢? 對代碼for5.1,可以把用&&連接復合語句拆分為兩條單獨的語句,寫成: 1 @echo off 2 set num=0 3 echo %num% 4 pause>nul 但是,這不是我們這次想要的結(jié)果。 對這兩段代碼都適用的辦法是:使用變量延遲擴展語句,讓變量的擴展行為延遲一下,從而獲取我們想要的值。 在這里,我們先來充下電,看看“變量擴展”又是怎么一回事。 用CN-DOS里批處理達人willsort的原話,那就是:“在許多可見的官方文檔中,均將使用一對百分號閉合環(huán)境變量以完成對其值的替換行為稱之為“擴展(expansion)”,這其實是一個第一方的概念,是從命令解釋器的角度進行稱謂的,而從我們使用者的角度來看,則可以將它看作是引用(Reference)、調(diào)用(Call)或者獲?。℅et)。”說得直白一點,所謂的“變量擴展”,實際上就是很簡單的這么一件事情:用具體的值去替換被引用的變量及緊貼在它左右的那對百分號。 既然只要延遲變量的擴展行為,就可以獲得我們想要的結(jié)果,那么,具體的做法又是怎樣的呢? 一般說來,延遲變量的擴展行為,可以有如下選擇: [1] 在適當位置使用 setlocal enabledelayedexpansion 語句; [2] 在適當?shù)奈恢檬褂?call 語句。 使用 setlocal enabledelayedexpansion 語句,那么,for5.1 和 for5.2 可以分別修改為: 1 @echo off2 setlocal enabledelayedexpansion3 set num=0 && echo !num!4 pause>nul 1 @echo off 2 set num=0 3 setlocal enabledelayedexpansion 4 for /f %%i in ('dir /a-d /b *.exe') do ( 5 set /a num+=1 6 echo num 當前的值是 !num! 7 ) 8 echo 當前目錄下共有 %num% 個exe文件 9 dir /a-d /b *.txt|findstr "test">nul&&(10 echo 存在含有 test 字符串的文本本件11 )||echo 不存在含有 test 字符串的文本文件12 if exist test.ini (13 echo 存在 test.ini 文件14 ) else echo 不存在 test.ini 文件15 pause>nul 使用第call語句,那么,for5.1 修改為: 1 @echo off2 set num=0&&call echo %%num%%3 pause>nul for5.2 修改為: 1 @echo off 2 set num=0 3 for /f %%i in ('dir /a-d /b *.exe') do ( 4 set /a num+=1 5 call echo num 當前的值是 %%num%% 6 ) 7 echo 當前目錄下共有 %num% 個exe文件 8 dir /a-d /b *.txt|findstr "test">nul&&( 9 echo 存在含有 test 字符串的文本本件10 )||echo 不存在含有 test 字符串的文本文件11 if exist test.ini (12 echo 存在 test.ini 文件13 ) else 不存在 test.ini 文件14 pause>nul 由此可見,如果使用 setlocal enabledelayedexpansion 語句來延遲變量,就要把原本使用百分號對閉合的變量引用改為使用感嘆號對來閉合;如果使用call語句,就要在原來命令的前部加上 call 命令,并把變量引用的單層百分號對改為雙層。 其中,因為call語句使用的是雙層百分號對,容易使人犯迷糊,所以用得較少,常用的是使用 setlocal enabledelayedexpansion 語句(set是設(shè)置的意思,local是本地的意思,enable是能夠的意思,delayed是延遲的意思,expansion是擴展的意思,合起來,就是:讓變量成為局部變量,并延遲它的擴展行為)。 通過上面的分析,我們可以知道: [1] 為什么要使用變量延遲?因為要讓復合語句內(nèi)部的變量實時感知到變量值的變化。 [2] 在哪些場合需要使用變量延遲語句?在復合語句內(nèi)部,如果某個變量的值發(fā)生了改變,并且改變后的值需要在復合語句內(nèi)部的其他地方被用到,那么,就需要使用變量延遲語句。而復合語句有:for語句、if……else語句、用連接符&、||和&&連接的語句、用管道符號|連接的語句,以及用括號括起來的、由多條語句組合而成的語句塊。最常見的場合,則是for語句和if……else語句。 [3] 怎樣使用變量延遲? 方法有兩種: ?、?使用 setlocal enabledelayedexpansion 語句:在獲取變化的變量值語句之前使用setlocal enabledelayedexpansion,并把原本使用百分號對閉合的變量引用改為使用感嘆號對來閉合; ② 使用 call 語句:在原來命令的前部加上 call 命令,并把變量引用的單層百分號對改為雙層。 “變量延遲”是批處理中一個十分重要的機制,它因預處理機制而生,用于復合語句,特別是大量使用于強大的for語句中。只有熟練地使用這一機制,才能在for的世界中如魚得水,讓自己的批處理水平更上一層樓。很多時候,對for的處理機制,我們一直是霧里看花,即使偶有所得,也只是只可意會難以言傳。希望大家反復揣摩,多加練習,很多細節(jié)上的經(jīng)驗,是只有通過大量的摸索才能得到的。 備注:整理本文的參考文獻《批處理for語句從入門到精通》 Good Good Study,Day Day Up. 順序 選擇 循環(huán) 總結(jié) |
|