| namejm [樓主] 昵稱(chēng) 塵土飛揚 ![]() ![]() ![]() ![]()
![]()
| 閉關(guān)ing,非誠勿擾。 尺有所短寸有所長(cháng),學(xué)好批處理沒(méi)商量; 考慮問(wèn)題復雜化,解決問(wèn)題簡(jiǎn)潔化。 |
[系列教程]for語(yǔ)句從入門(mén)到精通[2009.2.12更新]____________________________版主提醒____________________________ 為了避免影響技術(shù)討論、提高看帖的舒適性,請大家不要在此帖下跟 無(wú)實(shí)質(zhì)內容的口水帖,特別是純頂、純支持、純感謝、路過(guò)之類(lèi)的帖子, 管理人員將不定期清理此類(lèi)回帖,請大家多參與討論少灌水,與人方便, 終將給自己帶來(lái)方便,謝謝合作。 ________________________________________________________________ 批處理是一門(mén)簡(jiǎn)單的腳本語(yǔ)言,雖然不能獨當一面,但是,若作為工作中的輔助工具,絕對會(huì )讓大家有隨用隨寫(xiě)、稱(chēng)心如意的暢快感。 和其他語(yǔ)言相比,批處理語(yǔ)言有其先天性的優(yōu)勢: 1、系統自帶,無(wú)需另行安裝; 2、命令少,語(yǔ)句簡(jiǎn)潔,上手非???; 3、編寫(xiě)出來(lái)的腳本小巧玲瓏,隨寫(xiě)隨用; 但是,因為它以命令行方式工作,操作多有不便,在圖形界面大行其道的windows世界里,多多少少會(huì )讓大眾望而卻步;就算是對命令行有好感的新手,面對微軟有如天書(shū)的幫助文件,很多人也會(huì )敗下陣來(lái),因此,論壇里很多會(huì )員也發(fā)出了編寫(xiě)系統的批處理教程的呼聲。 編寫(xiě)系統的批處理新手教程,一直是論壇管理層討論的熱點(diǎn)問(wèn)題,但是,各位管理人員大多都有工作在身,而系統的教程涉及的面是如此之廣,面對如此浩大的工程,僅憑一兩個(gè)人的力量,是難以做好的,因此,本人退而求其次,此次發(fā)布的教程,以專(zhuān)題的形式編寫(xiě),日后人手漸多之后,再考慮組織人力編寫(xiě)全面的教程。 之所以選擇最難的for,一是覺(jué)得for最為強大,是大多數人最希望掌握的;二是若寫(xiě)其他命令教程,如果沒(méi)有for的基礎,展開(kāi)來(lái)講解會(huì )無(wú)從下手;三是for也是批處理中最復雜最難掌握的語(yǔ)句,把它攻克了,批處理的學(xué)習將會(huì )一片坦途。 這次的for語(yǔ)句系列教程,打算按照f(shuō)or語(yǔ)句的5種句式逐一展開(kāi),在講解 for /f 的時(shí)候,會(huì )穿插講解批處理中一個(gè)最為關(guān)鍵、也是新手最容易犯錯的概念:變量延遲,大綱如下: 引用:
閉關(guān)ing,非誠勿擾。 尺有所短寸有所長(cháng),學(xué)好批處理沒(méi)商量; 考慮問(wèn)題復雜化,解決問(wèn)題簡(jiǎn)潔化。 | |
|
|
| namejm [樓主] 昵稱(chēng) 塵土飛揚 ![]() ![]() ![]() ![]()
![]()
| 一、前言 在批處理中,for是最為強大的命令語(yǔ)句,它的出現,使得解析文本內容、遍歷文件路徑、數值遞增/遞減等操作成為可能;配合if、call、goto等流程控制語(yǔ)句,更是可以實(shí)現腳本復雜的自動(dòng)化、智能化操作;合理使用for語(yǔ)句,還能使代碼大為簡(jiǎn)化,免除各位編寫(xiě)大量重復語(yǔ)句之苦。而能否熟練使用for語(yǔ)句,已經(jīng)成為衡量一個(gè)人批處理水平高低最主要的標準。 在這個(gè)系列教程中,我將通過(guò)實(shí)際應用中頻繁出現的例子,帶領(lǐng)大家步入for語(yǔ)句的神奇之門(mén),一步步邁向for語(yǔ)句的魔幻殿堂,使得大家在實(shí)際的應用中,能獨立寫(xiě)出簡(jiǎn)潔高效的代碼,在批處理的世界里自由馳騁。 注意:以下的講解,都是基于簡(jiǎn)體中文版Windows XP Pro SP3的操作系統環(huán)境。 閉關(guān)ing,非誠勿擾。 尺有所短寸有所長(cháng),學(xué)好批處理沒(méi)商量; 考慮問(wèn)題復雜化,解決問(wèn)題簡(jiǎn)潔化。 |
| namejm [樓主] 昵稱(chēng) 塵土飛揚 ![]() ![]() ![]() ![]()
![]()
| ![]() ![]() 二、for語(yǔ)句的基本用法[2008.11.9更新] 正如色彩繽紛的七彩光芒是由紅綠藍三原色構成的一樣,最復雜的for語(yǔ)句,也有其基本形態(tài),它的模樣是這樣的: 在cmd窗口中: 復制內容到剪貼板 在批處理文件中: 代碼:for %I in (command1) do command2復制內容到剪貼板 之所以要區分cmd窗口和批處理文件兩種環(huán)境,是因為在這兩種環(huán)境下,命令語(yǔ)句表現出來(lái)的行為雖然基本一樣,但是在細節上還是稍有不同,最明顯的一個(gè)差異就是:在cmd窗口中,for之后的形式變量I必須使用單百分號引用,即%I;而在批處理文件中,引用形式變量I必須使用雙百分號,即%%I。為了方便起見(jiàn),若不是特別強調,以下的講解都以批處理文件環(huán)境為例。代碼:for %%I in (command1) do command2我們先來(lái)看一下for語(yǔ)句的基本要素都有些什么: 引用:1、for、in和do是for語(yǔ)句的關(guān)鍵字,它們三個(gè)缺一不可; [code1] 復制內容到剪貼板 保存為批處理文件并執行,將會(huì )在彈出的批處理窗口中看到這樣的信息:代碼:@echo off[result1] 引用:bbs.bathome.cn 是的,演示代碼永遠都只是演示而已,就像大多數高級語(yǔ)言的教科書(shū)一樣,在引導新手學(xué)習的時(shí)候,基本上都是千篇一律地告訴大家如何編寫(xiě)一個(gè)能顯示 hello world! 的窗口,從這些演示代碼中,你看不到它們具有多少實(shí)用性,你只是感到有點(diǎn)好奇:咦,居然彈出了一個(gè)窗口?片刻之后,你就會(huì )覺(jué)得索然無(wú)味。 那好吧,為了讓大家對for更加感興趣,我們先來(lái)分析一下for語(yǔ)句的一些注意事項,之后,再讓大家看看更為強大的for語(yǔ)句實(shí)例。 引用:1、for語(yǔ)句的形式變量I,可以換成26個(gè)字母中的任意一個(gè),這些字母會(huì )區分大小寫(xiě),也就是說(shuō),%%I和%%i會(huì )被認為不是同一個(gè)變量;形式變量I還可以換成其他的字符,但是,為了不與批處理中的%0~%9這10個(gè)形式變量發(fā)生沖突,請不要隨意把%%I替換為%%0 ~%%9中的任意一個(gè); 有了以上的基礎,我們再來(lái)看一個(gè)例子,這個(gè)例子修改了[code1]的部分內容,結果將大不一樣: [code2] 復制內容到剪貼板 和[code1]的執行結果[result1]相比,[result2]發(fā)生了如下變化:代碼:@echo off1、顯示結果分成了3行(不算最后一行中文提示); 2、每一行都從逗號處被切分; 如果把 bbs.bathome.cn 這個(gè)字符串中的點(diǎn)號換為空格、跳格或等號,執行結果將和example2的執行結果別無(wú)二致。 現在,我們來(lái)分析一下[code2]代碼中for語(yǔ)句的執行過(guò)程: 首先,for語(yǔ)句以逗號為分隔符,把 bbs,bathome,cn 這個(gè)字符串切分成三個(gè)元素:bbs、bathome和cn,由此決定了do后的語(yǔ)句將會(huì )被執行3次; 然后,第一次執行過(guò)程是這樣的:先把 bbs 這個(gè)字符串作為形式變量I的值,帶入do后的語(yǔ)句中加以執行,也就是執行 echo %%I 語(yǔ)句,此時(shí)的I值為bbs,因此,第一次執行的結果,將會(huì )在屏幕上顯示bbs這個(gè)字符串;第二次執行和第一次執行的過(guò)程是一樣的,只不過(guò)此時(shí)I的值已經(jīng)被替換為command1中的第二個(gè)元素了,也就是 bathome 這個(gè)字符串;如此循環(huán),當第三次echo執行完畢之后,整條for語(yǔ)句才算執行完畢,此時(shí),將執行下一條語(yǔ)句,也就是pause命令。 其實(shí),這個(gè)例子只比上一個(gè)例子多了一點(diǎn)花樣,有趣了那么一點(diǎn)點(diǎn):一條for語(yǔ)句的執行結果居然被分成了3行! 為了讓大家見(jiàn)識一下for的真正威力,本人絞盡腦汁,翻帖無(wú)數,不得要領(lǐng),萬(wàn)般無(wú)奈之下,只好亮出了塵封在箱底多年的一段代碼:檢測當前硬盤(pán)都有哪些分區^_^ [code3] 復制內容到剪貼板 這段代碼能檢測硬盤(pán)都有哪些分區,包括U盤(pán)和移動(dòng)硬盤(pán)的分區,但是,當光驅中有盤(pán)的時(shí)候,也會(huì )被列出來(lái),這是本代碼的一個(gè)缺憾,在以后的講解中,我將向大家講述如何消除這個(gè)瑕疵,敬請關(guān)注本系列的后續章節。代碼:@echo off高級應用: 想知道當前目錄下都有哪些文件嗎?請用下面的代碼: 復制內容到剪貼板 想列出當前目錄下所有的文本文件嗎?請用下面的代碼: 代碼:@echo off復制內容到剪貼板 想列出只用兩個(gè)字符作為文件名的文本文件嗎?請用下面的代碼: 代碼:@echo off復制內容到剪貼板 題外話(huà):代碼:@echo off1、列出當前目錄下各種文件的方法,最簡(jiǎn)單的還是用dir命令,但是,從以上代碼中,各位可以加深對for語(yǔ)句執行流程的理解(用到了通配符*和?); 2、注意:以上代碼不能列出含有隱藏或系統屬性的文件; 練習:用for語(yǔ)句建立test1.txt、test2.txt和test3.txt三個(gè)文本文件。 更全面的練習請看這個(gè)帖子:for語(yǔ)句從入門(mén)到精通配套練習題 閉關(guān)ing,非誠勿擾。 尺有所短寸有所長(cháng),學(xué)好批處理沒(méi)商量; 考慮問(wèn)題復雜化,解決問(wèn)題簡(jiǎn)潔化。 |
| namejm [樓主] 昵稱(chēng) 塵土飛揚 ![]() ![]() ![]() ![]()
![]()
| 三、文本解析顯神威:for /f 用法詳解[2009.2.10更新] 前言 for /f 是個(gè)十分強大的家伙。 如果說(shuō),for語(yǔ)句是批處理中最強大的語(yǔ)句的話(huà),那么,for /f 就是精華中的精華。 for /f 的強大,和它擁有眾多的開(kāi)關(guān)密切相關(guān)。因為開(kāi)關(guān)眾多,所以用法復雜,本章將分成若干小節,為大家逐一介紹強大的 for /f 語(yǔ)句。 (一) 為解析文本而生:for /f 的基本用法 所有的對象,無(wú)論是文件、窗體、還是控件,在所有的非機器語(yǔ)言看來(lái),無(wú)外乎都是形如"c:\test.txt"、"CWnd"之類(lèi)的文本信息;而所有的對象,具體的如ini文件中的某條配置信息、注冊表中的某個(gè)鍵值、數據庫中的某條記錄……都只有轉化為具有一定格式的文本信息,方可被代碼識別、操控??梢哉f(shuō),編程的很大一部分工作,都是在想方設法絞盡腦汁如何提取這些文本信息。 而提取文本信息,則是for /f的拿手好戲:讀取文件內容;提取某幾行字符;截取某個(gè)字符片段;對提取到的內容再切分、打亂、雜糅……只要你所能想到的花樣,for /f 都會(huì )想方設法幫你辦到,因為,for /f 就是被設計成專(zhuān)門(mén)用于解析文本的。 先來(lái)看個(gè)例子。 假如有個(gè)文本文件test.txt,內容如下: [txt1] 引用:論壇的目標是:不求最大,但求最好,做最實(shí)用的批處理論壇。 [code4] 復制內容到剪貼板 這段代碼,主要是讓你樹(shù)立這樣一種觀(guān)念:讀取文本文件的內容,請使用 for /f 語(yǔ)句!代碼:@echo off進(jìn)階話(huà)題:for /f 語(yǔ)句是把整個(gè)test.txt一次性顯示出來(lái)的? 在這段代碼中,雖然執行結果是把test.txt中的所有內容都顯示出來(lái)了,貌似 for /f 語(yǔ)句是把整個(gè)test.txt一次性顯示到屏幕上,實(shí)際上并非如此。 無(wú)論for語(yǔ)句做何種變化,它的執行過(guò)程仍然遵循基本的for流程:依次處理每個(gè)元素,直到所有的元素都被處理為止。只不過(guò)在for /f語(yǔ)句中,這里的元素是指文件中的每一行,也就是說(shuō),for /f 語(yǔ)句是以行為單位處理文本文件的。這是一條極為重要的規則,在上一章中也強調過(guò)它的重要性,希望在接下來(lái)的學(xué)習過(guò)程中,你能時(shí)刻牢記這一原則,那么,很多問(wèn)題將會(huì )迎刃而解。以下是驗證這一說(shuō)法的演示代碼(在[code4]的基礎上添加了&pause語(yǔ)句): [code5] 復制內容到剪貼板 (二) 切分字符串的利器:delims=代碼:@echo off也許你對[code4]這段代碼不屑一顧:不就是把test.txt的內容顯示出來(lái)了么?好像用處不大啊。 好吧,我們來(lái)玩個(gè)魔術(shù)。 還是[txt1]這段文本,把[code4]改造一下: [code6] 復制內容到剪貼板 再次運行test.cmd,看到什么變化了嗎?!代碼:@echo off[result2] 復制內容到剪貼板 結果,你驚奇地發(fā)現,每行第一個(gè)逗號之后的所有內容都不見(jiàn)了(如果有不存在逗號的行,則保留原樣),也就說(shuō),你成功地提取到了每行第一個(gè)逗號之前的所有內容!代碼:論壇的目標是:不求最大試想一下,這段代碼會(huì )有什么用呢? 如果別人給了你一個(gè)軟件清單,每行都是"英文軟件名(逗號)中文軟件名"的格式,而你卻只想保留英文名的時(shí)候,這段代碼將是多么有用??!再假設,有這么一個(gè)IP文件,第一列是數字格式的IP地址,第二列是具體的空間地址,列與列之間用逗號分隔,而你想提取其中數字格式的IP,呵呵,我不說(shuō)你也知道該怎么辦了吧? 要是文本內容不是以逗號分隔,而是以其他符號分隔,那么,把"delims=,"的逗號換成相應的符號就可以了。 在這里,我們引入了一個(gè)新的開(kāi)關(guān):"delims=,",它的含義是:以逗號作為被處理的字符串的分隔符號。 在批處理中,指定分隔符號的方法是:添加一個(gè)形如 "delims=符號列表" 的開(kāi)關(guān),這樣,被處理的每行字符串都會(huì )被符號列表中羅列出來(lái)的符號切分開(kāi)來(lái)。 需要注意的是:如果沒(méi)有指定"delims=符號列表"這個(gè)開(kāi)關(guān),那么,for /f 語(yǔ)句默認以空格鍵或跳格鍵作為分隔符號。請把[txt1]中不同位置上的標點(diǎn)符號改為空格或跳格,再運行[code4]試試。 進(jìn)階話(huà)題:如果我要指定的符號不止一個(gè),該怎么辦? 在上面的講解中,我提到了指定分隔符號的方法:添加一個(gè)形如"delims=符號列表"的開(kāi)關(guān)。不知道你注意到?jīng)]有,我的說(shuō)法是"符號列表"而非"符號",這是大有講究的,因為,你可以一次性指定多個(gè)分隔符號! 還是以[txt1]為例,把[code6]再改造一下: [code7] 復制內容到剪貼板 結果顯示:代碼:@echo off[result3] 復制內容到剪貼板 這樣,第一個(gè)點(diǎn)號或第一個(gè)逗號之前的內容都被提取出來(lái)了。代碼:論壇的目標是:不求最大[code7]的執行過(guò)程是:逐行讀取test.txt中的內容,以點(diǎn)號和逗號切分每一行的內容(不存在點(diǎn)號和逗號的行,則不再切分,為了描述的方便,我們把被點(diǎn)號或逗號切分的一個(gè)一個(gè)的字符串片段,稱(chēng)之為節),然后,for /f 會(huì )提取第一節的內容作為最終結果,顯示在屏幕上。需要注意的是,在這里,所有行的字符串被切分成了兩個(gè)以上的節,但是,[code7]的代碼只會(huì )提取第一節字符串的內容,因為 for /f 語(yǔ)句默認只提取第一節的符串。 (三) 定點(diǎn)提?。簍okens= 上一節在講解 delims= 的時(shí)候,我一再強調 for /f 默認只能提取到第一節的內容,現在我們來(lái)思考一個(gè)問(wèn)題:如果我要提取的內容不在第一節上,那怎么辦? 這回,就該輪到 tokens= 出馬了。 tokens= 后面一般跟的是數字,如 tokens=2,也可以跟多個(gè),但是每個(gè)數字之間用逗號分隔,如 tokens=3,5,8,它們的含義分別是:提取第2節字符串、提取第3、第5和第8節字符串。注意,這里所說(shuō)的“節”,是由 delims= 這一開(kāi)關(guān)劃分的,它的內容并不是一成不變的。 下面來(lái)看一個(gè)例子: [txt2] 復制內容到剪貼板 對[txt2]這段文本,假設它們保存在文件test.txt中,如果我想提取“學(xué)好批處理沒(méi)商量”這句話(huà),該如何寫(xiě)代碼呢?代碼:尺有所短,寸有所長(cháng),學(xué)好批處理沒(méi)商量,考慮問(wèn)題復雜化,解決問(wèn)題簡(jiǎn)潔化。我們稍微觀(guān)察一下[txt2]就會(huì )發(fā)現,如果以逗號作為切分符號,就正好可以把“學(xué)好批處理沒(méi)商量”化為單獨的一“節”,結合上一節的講解,我們知道,"delims=," 這個(gè)開(kāi)關(guān)是不可缺少的,而要提取的內容在以逗號切分的第3節上,那么,tokens= 后面的數字就應該是3了,最終的代碼如下: [code8] 復制內容到剪貼板 如果我們現在要提取的不只一個(gè)“節”,而是多個(gè),那又怎么辦呢?比如,要提取以逗號切分的第2節和第5節字符串,是寫(xiě)成這樣嗎?代碼:@echo off[code9] 復制內容到剪貼板 運行批處理后發(fā)現,執行結果只顯示了第2節的內容。代碼:@echo off原來(lái),echo 后面的 %%i 只接收到了 tokens=2,5 中第一個(gè)數值2所代表的那個(gè)字符串,而第二個(gè)數值5所代表的字符串因為沒(méi)有變量來(lái)接收,所以就無(wú)法在執行結果中顯示出來(lái)了。 那么,要如何接收 tokens= 后面多個(gè)數值所指代的內容呢? for /f 語(yǔ)句對這種情況做如下規定: 如果 tokens= 后面指定了多個(gè)數字,如果形式變量為%%i,那么,第一個(gè)數字指代的內容用第一個(gè)形式變量%%i來(lái)接收,第二個(gè)數字指代的內容用第二個(gè)形式變量%%j來(lái)接收,第三個(gè)數字指代的內容用第三個(gè)形式變量%%k來(lái)接收……第N個(gè)數字指代的內容用第N個(gè)形式變量來(lái)接收,其中,形式變量遵循字母的排序,第N個(gè)形式變量具體是什么符號,由第一個(gè)形式變量來(lái)決定:如果第一個(gè)形式變量是%%i,那么,第二個(gè)形式變量就是%%j;如果第一個(gè)形式變量用的是%%x,那么,第二個(gè)形式變量就是%%y。 現在回頭去看[code9],你應該知道如何修改才能滿(mǎn)足題目的要求了吧?修改結果如下: [code10] 復制內容到剪貼板 如果有這樣一個(gè)要求:顯示[txt2]中的內容,但是逗號要替換成空格,如何編寫(xiě)代碼?代碼:@echo off結合上面所學(xué)的內容,稍加思索,你可能很快就得出了答案: [code11] 復制內容到剪貼板 寫(xiě)完之后,你可能意識到這樣一個(gè)問(wèn)題:假如要提取的“節”數不是5,而是10,或者20,或者更多,難道我也得從1寫(xiě)到10、20或者更多嗎?有沒(méi)有更簡(jiǎn)潔的寫(xiě)法呢?代碼:@echo off答案是有的,那就是:如果要提取的內容是連續的多“節”的話(huà),那么,連續的數字可以只寫(xiě)最小值和最大值,中間用短橫連接起來(lái)即可,比如 tokens=1,2,3,4,5 可以簡(jiǎn)寫(xiě)為 tokens=1-5 。 還可以把這個(gè)表達式寫(xiě)得更復雜一點(diǎn):tokens=1,2-5,tokens=1-3,4,5,tokens=1-4,5……怎么方便就怎么寫(xiě)吧。 大家可能還看到一種比較怪異的寫(xiě)法: [code12] 復制內容到剪貼板 結果,第一個(gè)逗號不見(jiàn)了,取代它的是一個(gè)空格符號,其余部分保持不變。代碼:for /f "delims=, tokens=1,*" %%i in (test.txt) do echo %%i %%j其中奧妙就在這個(gè)星號上面。 tokens=后面所接的星號具備這樣的功能:字符串從左往右被切分成緊跟在*之前的數值所表示的節數之后,字符串的其余部分保持不變,整體被*所表示的一個(gè)變量接收。 理論講解是比較枯燥的,特別是為了嚴密起見(jiàn),還使用了很多限定性的修飾詞,導致句子很長(cháng),增加了理解的難度,我們還是結合[code12]來(lái)講解一下吧。 [txt2] 的內容被切分,切分符號為逗號,當切分完第一節之后,切分動(dòng)作不再繼續下去,因為 tokens=1,* 中,星號前面緊跟的是數字1;第一節字符串被切分完之后,其余部分字符串不做任何切分,整體作為第二節字符串,這樣,[txt2]就被切分成了兩節,分別被變量%%i和變量%%j接收。 以上幾種切分方式可以結合在一起使用。不知道下面這段代碼的含義你是否看得懂,如果看不懂的話(huà),那就運行一下代碼,然后反復揣摩,你一定會(huì )更加深刻地理解本節所講解的內容的: [code13] 復制內容到剪貼板 (四) 跳過(guò)無(wú)關(guān)內容,直奔主題:skip=n代碼:@echo off很多時(shí)候,有用的信息并不是貫穿文本內容的始終,而是位于第N行之后的行內,為了提高文本處理的效率,或者不受多余信息的干擾,for /f 允許你跳過(guò)這些無(wú)用的行,直接從第N+1行開(kāi)始處理,這個(gè)時(shí)候,就需要使用參數 skip=n,其中,n是一個(gè)正整數,表示要跳過(guò)的行數。例如: [code14] 復制內容到剪貼板 這段代碼將跳過(guò)頭兩行內容,從第3行起顯示test.txt中的信息。代碼:@echo off(五) 忽略以指定字符打頭的行:eol= 在cmd窗口中敲入:for /?,相關(guān)的解釋為: 引用:eol=c - 指一個(gè)行注釋字符的結尾(就一個(gè)) 引用:FOR /F "eol=; tokens=2,3* delims=, " %i in (myfile.txt) do @echo %i %j %k 實(shí)踐是檢驗真理的唯一標準,還是用代碼來(lái)檢驗一下eol的作用吧: [code15] 復制內容到剪貼板 結果,那些以分號打頭的行沒(méi)有顯示出來(lái)。代碼:@echo off由此可見(jiàn),第二條解釋是正確的,eol= 的準確含義是:忽略以指定字符打頭的行。而第一條的“結尾”純屬微軟在信口開(kāi)河。 那么,“(就一個(gè))”又作何解釋呢? 試試這個(gè)代碼: [code16] 復制內容到剪貼板 此時(shí),屏幕上出現 此時(shí)不應有 ;"。 的報錯信息??梢?jiàn),在指定字符的時(shí)候,只能指定1個(gè)——在很多時(shí)候,我對這樣的設計頗有微詞而又無(wú)可奈何:為什么只能指定1個(gè)而不是多個(gè)?要忽略多個(gè)還得又是if又是findstr加管道來(lái)多次過(guò)濾,那效率實(shí)在太低下了——能用到的功能基本上都提供,但是卻又做不到更好,批處理,你的功能為什么那么弱?代碼:@echo off不知道大家注意到?jīng)]有,如果test.txt中有以分號打頭的行,那么,這些行在代碼[code14]的執行結果中將憑空消失。 原來(lái),for /f 語(yǔ)句是默認忽略以分號打頭的行內容的,正如它默認以空格鍵或跳格鍵作為字符串的切分字符一樣。 很多時(shí)候,我們可以充分利用這個(gè)特點(diǎn),比如,在設計即將用for讀取的配置文件的時(shí)候,可以在注釋文字的行首加上分號,例如在編寫(xiě)病毒文件查殺代碼的時(shí)候,可以通過(guò)for語(yǔ)句來(lái)讀取病毒文件列表,那么,病毒文件列表.ini這個(gè)配置文件可以這樣寫(xiě): 引用:;以下是常見(jiàn)的病毒文件,請見(jiàn)一個(gè)殺一個(gè)^_^ 1、為eol=指定另外一個(gè)字符; 2、使用 for /f "eol=" 語(yǔ)句,也就是說(shuō),強制指定字符為空,就像對付delims=一樣。 ?。┤绾螞Q定該使用 for /f 的哪種句式?(兼談usebackq的使用) for /f %%i in (……) do (……) 語(yǔ)句有好幾種變形語(yǔ)句,不同之處在于第一個(gè)括號里的內容:有的是用單引號括起來(lái),有的是用雙引號包住,有的不用任何符號包裹,具體格式為: 引用:1、for /f %%i in (文件名) do (……) 實(shí)際上,當我在上面羅列這些語(yǔ)句的時(shí)候,已經(jīng)有所提示了,不知道你是否注意到了。 如果你一時(shí)無(wú)法參透其中奧妙,那也無(wú)妨,請聽(tīng)我一一道來(lái)便是。 1、當你希望讀取文本文件中的內容的話(huà),第一個(gè)括號中不用任何符號包裹,應該使用的是第1條語(yǔ)句;例如:你想顯示test.txt中的內容,那么,就使用 for /f %%i in (test.txt) do echo %%i; 2、當你讀取的是命令語(yǔ)句執行結果中的內容的話(huà),第一個(gè)括號中的命令語(yǔ)句必須使用單引號包裹,應該使用的是第2條語(yǔ)句;例如:你想顯示當前目錄下文件名中含有test字符串的文本文件的時(shí)候,應該使用 for /f %%i in ('dir /a-d /b *test*.txt') do echo %%i 這樣的語(yǔ)句; 3、當你要處理的是一個(gè)字符串的時(shí)候,第一個(gè)括號中的內容必須用雙引號括起來(lái),應該是用的是第3條語(yǔ)句;例如:當你想把bbs.bathome.cn這串字符中的點(diǎn)號換為短橫線(xiàn)并顯示出來(lái)的話(huà),可以使用 for /f "delims=. tokens=1-3" %%i in ("bbs.bathome.cn") do echo %%i-%%j-%%k 這樣的語(yǔ)句。 很顯然,第一個(gè)括號里是否需要用符號包裹起來(lái),以及使用什么樣的符號包裹,取決于要處理的對象屬于什么類(lèi)型:如果是文件,則無(wú)需包裹;如果是命令語(yǔ)句,則用單引號包裹;如果是字符串,則使用雙引號括起來(lái)。 當然,事情并不是絕對如此,如果細心的你想到了批處理中難纏的特殊字符,你肯定會(huì )頭大如斗。 或許你頭腦中靈光一閃,已經(jīng)想到了一個(gè)十分頭痛的問(wèn)題:在第1條語(yǔ)句中,如果文件名中含有空格或&,該怎么辦? 照舊嗎? 拿個(gè)叫 test 1.txt 的文件來(lái)試試。 你很快寫(xiě)好了代碼,新建文件-->碼字-->保存為批處理,前后費時(shí)不到1分鐘: [code17] 復制內容到剪貼板 你興沖沖地雙擊批處理,運行后,屏幕上出現了可恥的報錯信息:系統找不到文件 test 。代碼:@echo off當你把 test 1.txt 換成 test&1.txt 后,更怪異的事情發(fā)生了:CMD窗口在你眼前一閃而過(guò),然后,優(yōu)雅地消失了。 你可能覺(jué)得自己的代碼寫(xiě)錯了某些符號,你再仔細的檢查了一次,確認沒(méi)有筆誤,然后,你再次雙擊批處理,結果問(wèn)題照舊;你開(kāi)始懷疑其他程序對它可能有影響,于是關(guān)掉其他窗口,再運行了一次,問(wèn)題依舊;你不服氣地連續運行了好幾次,還是同樣的結果。 怪哉! 你一拍大腿,猛然想起了一件事:當路徑中含有特殊字符的時(shí)候,應該使用引號把路徑括起來(lái)。對,就是它了! 但是,當你把代碼寫(xiě)出來(lái)之后,你很快就焉了:for /f %%i in ("test 1.txt") do echo %%i,這不就是上面提到的第3條 for /f 命令的格式嗎?批處理會(huì )把 test 1.txt 這個(gè)文件名識別為字符串??! 你百無(wú)聊賴(lài)地在CMD窗口中輸入 for /? ,并重重地敲下了回車(chē),漫無(wú)目的地在幫助信息中尋找,希望能找到點(diǎn)什么。 結果還真讓你到了點(diǎn)什么。 你看到了這樣的描述: 引用:usebackq - 指定新語(yǔ)法已在下類(lèi)情況中使用: 還好,下面有個(gè)例子,并配有簡(jiǎn)單的說(shuō)明: 引用:
回過(guò)頭去再看那段關(guān)于usebackq的描述,字斟句酌,反復揣摩,終于被你破譯了天機:usebackq 是一個(gè)增強型參數,當使用了這個(gè)參數之后,原來(lái)的for語(yǔ)句中第一個(gè)括號內的寫(xiě)法要做如下變動(dòng):如果第一個(gè)括號里的對象是一條命令語(yǔ)句的話(huà),原來(lái)的單引號'要改為后引號`;如果第一個(gè)括號里的對象是字符串的話(huà),原來(lái)的雙引號"要改為單引號';如果第一個(gè)括號里的對象是文件名的話(huà),要用雙引號"括起來(lái)。 驗證一下,把[code17]改寫(xiě)成如下代碼: [code18] 復制內容到剪貼板 測試通過(guò)!代碼:@echo off此時(shí),你很可能會(huì )仰天長(cháng)嘆:Shit,微軟這該死的機器翻譯! 至于把[code17]代碼中的空格換成&后,CMD窗口會(huì )直接退出,那是因為&是復合語(yǔ)句的連接符,CMD在預處理的時(shí)候,會(huì )優(yōu)先把&前后兩部分作為兩條語(yǔ)句來(lái)解析,而不是大家想象中的一條完整的for語(yǔ)句,從而產(chǎn)生了嚴重的語(yǔ)法錯誤。因為牽涉到預處理機制問(wèn)題,不屬于本節要討論的內容,在此不做詳細講解。 這個(gè)時(shí)候,我們會(huì )吃驚地發(fā)現,區區一條for語(yǔ)句,竟然有多達6種句型: 引用:1、for /f %%i in (文件名) do (……) 好在后3種情形并不常用,所以,牢牢掌握好前三種句型的適用情形就可以了,否則,要在這么多句型中確定選擇哪一條語(yǔ)句來(lái)使用,還真有點(diǎn)讓人頭腦發(fā)懵。 至于 for /f 為什么要增加usebacq參數,我只為第4條語(yǔ)句找到了合理的解釋?zhuān)簽榱思嫒菸募兴鶐У目崭窕?amp;。它在第5、6條語(yǔ)句中為什么還有存在的必要,我也不是很明白,這有待于各位去慢慢發(fā)現。 閉關(guān)ing,非誠勿擾。 尺有所短寸有所長(cháng),學(xué)好批處理沒(méi)商量; 考慮問(wèn)題復雜化,解決問(wèn)題簡(jiǎn)潔化。 |
| namejm [樓主] 昵稱(chēng) 塵土飛揚 ![]() ![]() ![]() ![]()
![]()
| (七)變量延遲詳解[2009.2.12更新] 變量延遲在for語(yǔ)句中起著(zhù)至關(guān)重要的作用,不只是在for語(yǔ)句中,在其他的復合語(yǔ)句中,它也在幕后默默地工作著(zhù),為了突出它的重要性,本節內容在單獨的樓層中發(fā)出來(lái),希望引起大家的重視。 對于批處理新手而言,“變量延遲”這個(gè)概念很可能聞所未聞,但是,它卻像一堵橫亙在你前進(jìn)道路上的無(wú)形高墻,你感受不到它的存在,但當你試圖往前沖時(shí),它會(huì )把你狠狠地彈回來(lái),讓你無(wú)法逾越、無(wú)功而返;而一旦找到了越過(guò)它的方法,你就會(huì )發(fā)現,在for的世界里,前面已經(jīng)是一片坦途,而你對批處理的理解,又上升到了一個(gè)新的境界。 例如,你編寫(xiě)了這樣一個(gè)代碼: [code19] 復制內容到剪貼板 你的本意是想對變量num賦值之后,再把這個(gè)值顯示出來(lái),結果,顯示出來(lái)的并不是0,而是顯示:ECHO 處于關(guān)閉狀態(tài)。代碼:@echo off之所以會(huì )出錯,是因為“變量延遲”這個(gè)家伙在作怪。 在講解變量延遲之前,我們需要了解一下批處理的執行過(guò)程,它將有助于我們深入理解變量延遲。 批處理的執行過(guò)程是怎樣的呢? “自上而下,逐條執行”,我想,這個(gè)經(jīng)典的說(shuō)法大家都已經(jīng)耳熟能詳了,沒(méi)事的時(shí)候倒著(zhù)念,也還別有一番古韻呢^_^,但是,我想問(wèn)大家的是,大家真的深刻地理解了這句話(huà)的含義了嗎? “自上而下”,這一條和我們本節的講解關(guān)系不大,暫時(shí)略過(guò)不說(shuō),后一條,“逐條執行”和變量延遲有著(zhù)莫大的干系,它是我們本節要關(guān)注的重點(diǎn)。 很多人往往認為一行代碼就是一條語(yǔ)句,從而把“逐條執行”與“逐行執行”等同起來(lái),這就大錯特錯了。 莫非“逐條執行”里暗藏著(zhù)玄機? 正是如此。 “逐條”并不等同于“逐行”。這個(gè)“條”,是“一條完整的語(yǔ)句”的意思,并不是指“一行代碼”。在批處理中,是不是一條完整的語(yǔ)句,并不是以行來(lái)論的,而是要看它的作用范圍。 什么樣的語(yǔ)句才算“一條完整的語(yǔ)句”呢? 1、在復合語(yǔ)句中,整個(gè)復合語(yǔ)句是一條完整的語(yǔ)句,而無(wú)論這個(gè)復合語(yǔ)句占用了多少行的位置。常見(jiàn)的復合語(yǔ)句有:for語(yǔ)句、if……else語(yǔ)句、用連接符&、||和&&連接的語(yǔ)句,用管道符號|連接的語(yǔ)句,以及用括號括起來(lái)的、由多條語(yǔ)句組合而成的語(yǔ)句塊; 2、在非復合語(yǔ)句中,如果該語(yǔ)句占據了一行的位置,則該行代碼為一條完整的語(yǔ)句。 例如: [code20] 復制內容到剪貼板 上面的代碼共有14行,但是只有完整的語(yǔ)句只有7條,它們分別是:代碼:@echo off第1條:第1行的echo語(yǔ)句; 第2條:第2行的set語(yǔ)句; 第3條:第3、4、5、6行上的for復合語(yǔ)句; 第4條:第7行的echo語(yǔ)句; 第5條:第8、9、10行上用&&和||連接的復合語(yǔ)句; 第6條:第11、12、13行上的if……else復合語(yǔ)句; 第7條:第14行上的pause語(yǔ)句。 在這里,我之所以要花這么長(cháng)的篇幅來(lái)說(shuō)明一行代碼并不見(jiàn)得就是一條語(yǔ)句,是因為批處理的執行特點(diǎn)是“逐條”執行而不是“逐行”執行,澄清了這個(gè)誤解,將會(huì )更加理解批處理的預處理機制。 在代碼“逐條”執行的過(guò)程中,cmd.exe這個(gè)批處理解釋器會(huì )對每條語(yǔ)句做一些預處理工作,這就是批處理中大名鼎鼎的“預處理機制”。預處理的大致情形是這樣的:首先,把一條完整的語(yǔ)句讀入內存中(不管這條語(yǔ)句有多少行,它們都會(huì )被一起讀入),然后,識別出哪些部分是命令關(guān)鍵字,哪些是開(kāi)關(guān)、哪些是參數,哪些是變量引用……如果代碼語(yǔ)法有誤,則給出錯誤提示或退出批處理環(huán)境;如果順利通過(guò),接下來(lái),就把該條語(yǔ)句中所有被引用的變量及變量?jì)蛇叺陌俜痔枌?,用這條語(yǔ)句被讀入內存之就已經(jīng)賦予該變量的具體值來(lái)替換……當所有的預處理工作完成之后,批處理才會(huì )執行每條完整語(yǔ)句內部每個(gè)命令的原有功能。也就是說(shuō),如果命令語(yǔ)句中含有變量引用(變量及緊鄰它左右的百分號對),并且某個(gè)變量的值在命令的執行過(guò)程中被改變了,即使該條語(yǔ)句內部的其他地方也用到了這個(gè)變量,也不會(huì )用最新的值去替換它們,因為某條語(yǔ)句在被預處理的時(shí)候,所有的變量引用都已經(jīng)被替換成字符串常量了,變量值在復合語(yǔ)句內部被改變,不會(huì )影響到語(yǔ)句內部的其他任何地方。 順便說(shuō)一下,運行代碼[code20]之后,將在屏幕上顯示當前目錄下有多少個(gè)exe文件,是否存在含有 test 字符串的文本文件,以及是否存在 test.ini 這個(gè)文件等信息。讓很多人百思不得其解的是:如果當前目錄下存在exe文件,那么,有多少個(gè)exe文件,屏幕上就會(huì )提示多少次 "num 當前的值是 0" ,而不是顯示1到N(N是exe文件的個(gè)數)。 結合上面兩個(gè)例子,我們再來(lái)分析一下,為什么這兩段代碼的執行結果和我們的期望有一些差距。 在[code19]中,set num=0&&echo %num%是一條復合語(yǔ)句,它的含義是:把0賦予變量num,成功后,顯示變量num的值。 雖然是在變量num被賦值成功后才顯示變量num的值,但是,因為這是一條復合語(yǔ)句,在預處理的時(shí)候,&&后的%num%只能被set語(yǔ)句之前的語(yǔ)句賦予變量num的具體值來(lái)替換,而不能被復合語(yǔ)句內部、&&之前的set語(yǔ)句對num所賦予的值來(lái)替換,可見(jiàn),此num非彼num??墒?,在這條復合語(yǔ)句之前,我們并沒(méi)有對變量num賦值,所以,&&之后的%num%是空值,相當于在&&之后只執行了 echo 這一命令,所以,會(huì )顯示 echo 命令的當前狀態(tài),而不是顯示變量num的值(雖然該變量的值被set語(yǔ)句改變了)。 在[code20]中,for語(yǔ)句的含義是:列舉當前目錄下的exe文件,每發(fā)現一個(gè)exe文件,變量num的值就累加1,并顯示變量num的值。 看了對[code19]的分析之后,再來(lái)分析[code20]就不再那么困難了:第3、4、5行上的代碼共同構成了一條完整的for語(yǔ)句,而語(yǔ)句"echo num 當前的值是 %num%"與"set /a num+=1"同處復合語(yǔ)句for的內部,那么,第4行上set改變了num的值之后,并不能對第5行上的變量num有任何影響,因為在預處理階段,第5行上的變量引用%num%已經(jīng)被在for之前就賦予變量num的具體值替換掉了,它被替換成了0(是被第2行上的set語(yǔ)句賦予的)。 如果想讓代碼[code19]的執行結果中顯示&&之前賦予num的值,讓代碼[code20]在列舉exe文件的時(shí)候,從1到N地顯示exe文件的數量,那又該怎么辦呢? 對代碼[code19],可以把用&&連接復合語(yǔ)句拆分為兩條單獨的語(yǔ)句,寫(xiě)成: 復制內容到剪貼板 但是,這不是我們這次想要的結果。代碼:@echo off對這兩段代碼都適用的辦法是:使用變量延遲擴展語(yǔ)句,讓變量的擴展行為延遲一下,從而獲取我們想要的值。 在這里,我們先來(lái)充下電,看看“變量擴展”有是怎么一回事。 用CN-DOS里批處理達人willsort的原話(huà),那就是:“在許多可見(jiàn)的官方文檔中,均將使用一對百分號閉合環(huán)境變量以完成對其值的替換行為稱(chēng)之為“擴展(expansion)”,這其實(shí)是一個(gè)第一方的概念,是從命令解釋器的角度進(jìn)行稱(chēng)謂的,而從我們使用者的角度來(lái)看,則可以將它看作是引用(Reference)、調用(Call)或者獲?。℅et)。”(見(jiàn):什么情況下該使用變量延遲?http://www.cn-dos.net/forum/viewthread.php?tid=20733)說(shuō)得直白一點(diǎn),所謂的“變量擴展”,實(shí)際上就是很簡(jiǎn)單的這么一件事情:用具體的值去替換被引用的變量及緊貼在它左右的那對百分號。 既然只要延遲變量的擴展行為,就可以獲得我們想要的結果,那么,具體的做法又是怎樣的呢? 一般說(shuō)來(lái),延遲變量的擴展行為,可以有如下選擇: 1、在適當位置使用 setlocal enabledelayedexpansion 語(yǔ)句; 2、在適當的位置使用 call 語(yǔ)句。 使用 setlocal enabledelayedexpansion 語(yǔ)句,那么,[code19]和[code20]可以分別修改為: 復制內容到剪貼板 代碼:@echo off復制內容到剪貼板 使用第call語(yǔ)句,那么,[code19]和[code20]可以分別修改為: 代碼:@echo off復制內容到剪貼板 代碼:復制內容到剪貼板 由此可見(jiàn),如果使用 setlocal enabledelayedexpansion 語(yǔ)句來(lái)延遲變量,就要把原本使用百分號對閉合的變量引用改為使用感嘆號對來(lái)閉合;如果使用call語(yǔ)句,就要在原來(lái)命令的前部加上 call 命令,并把變量引用的單層百分號對改為雙層。 其中,因為call語(yǔ)句使用的是雙層百分號對,容易使人犯迷糊,所以用得較少,常用的是使用 setlocal enabledelayedexpansion 語(yǔ)句(set是設置的意思,local是本地的意思,enable是能夠的意思,delayed是延遲的意思,expansion是擴展的意思,合起來(lái),就是:讓變量成為局部變量,并延遲它的擴展行為)。代碼:通過(guò)上面的分析,我們可以知道: 1、為什么要使用變量延遲?因為要讓復合語(yǔ)句內部的變量實(shí)時(shí)感知到變量值的變化。 2、在哪些場(chǎng)合需要使用變量延遲語(yǔ)句?在復合語(yǔ)句內部,如果某個(gè)變量的值發(fā)生了改變,并且改變后的值需要在復合語(yǔ)句內部的其他地方被用到,那么,就需要使用變量延遲語(yǔ)句。而復合語(yǔ)句有:for語(yǔ)句、if……else語(yǔ)句、用連接符&、||和&&連接的語(yǔ)句、用管道符號|連接的語(yǔ)句,以及用括號括起來(lái)的、由多條語(yǔ)句組合而成的語(yǔ)句塊。最常見(jiàn)的場(chǎng)合,則是for語(yǔ)句和if……else語(yǔ)句。 3、怎樣使用變量延遲? 方法有兩種: ?、?使用 setlocal enabledelayedexpansion 語(yǔ)句:在獲取變化的變量值語(yǔ)句之前使用setlocal enabledelayedexpansion,并把原本使用百分號對閉合的變量引用改為使用感嘆號對來(lái)閉合; ?、?使用 call 語(yǔ)句:在原來(lái)命令的前部加上 call 命令,并把變量引用的單層百分號對改為雙層。 “變量延遲”是批處理中一個(gè)十分重要的機制,它因預處理機制而生,用于復合語(yǔ)句,特別是大量使用于強大的for語(yǔ)句中。只有熟練地使用這一機制,才能在for的世界中如魚(yú)得水,讓自己的批處理水平更上一層樓。很多時(shí)候,對for的處理機制,我們一直是霧里看花,即使偶有所得,也只是只可意會(huì )難以言傳。希望大家反復揣摩,多加練習,很多細節上的經(jīng)驗,是只有通過(guò)大量的摸索才能得到的。Good Luck! 本節內容在原理上參考了這篇文章:什么情況下該使用變量延遲?http://www.cn-dos.net/forum/viewthread.php?tid=20733,在本論壇中的地址是:http://bbs.bathome.cn/viewthread.php?tid=2899 特別鳴謝:willsort。 閉關(guān)ing,非誠勿擾。 尺有所短寸有所長(cháng),學(xué)好批處理沒(méi)商量; 考慮問(wèn)題復雜化,解決問(wèn)題簡(jiǎn)潔化。 |
聯(lián)系客服
微信登錄中...
請勿關(guān)閉此頁(yè)面




