在批處理中,for 是最爲強大的命令語句,它的出現,使得解析文本內容、遍歷文件路徑、數值遞增/遞減等操作成爲可能;配合if、call、goto 等流程控制語句,更是可以實現腳本複雜的自動化、智能化操作;合理使用for 語句,還能使代碼大爲簡化,免除各位編寫大量重複語句之苦。而能否熟練使用for 語句,已經成爲衡量一個人批處理水平高低最主要的標準。
在這個系列教程中,我將通過實際應用中頻繁出現的例子,帶領大家步入for語句的神奇之門,一步步邁向for 語句的魔幻殿堂,使得大家在實際的應用中,能獨立寫出簡潔高效的代碼,在批處理的世界裏自由馳騁。
注意:以下的講解,都是基於簡體中文Windows XP Pro SP3 的操作系統環境
二、for 語句的基本用法
正如色彩繽紛的七彩光芒是由紅綠藍三原色構成的一樣,最複雜的for 語句,
也有其基本形態,它的模樣是這樣的:
在cmd 窗口中:for %I in (command1) do command2
在批處理文件中:for %%I in (command1) do command2
之所以要區分 cmd 窗口和批處理文件兩種環境,是因爲在這兩種環境下,
命令語句表現出來的行爲雖然基本一樣,但是在細節上還是稍有不同,最明顯的
一個差異就是:在cmd 窗口中,for 之後的形式變量I 必須使用單百分號引用,
即%I;而在批處理文件中,引用形式變量I 必須使用雙百分號,即%%I。爲了方
便起見,若不是特別強調,以下的講解都以批處理文件環境爲例。
我們先來看一下for 語句的基本要素都有些什麼:
1、for、in 和do 是for 語句的關鍵字,它們三個缺一不可;
2、%%I 是for 語句中對形式變量的引用,就算它在do 後的語句中沒有參與
語句的執行,也是必須出現的;
3、in 之後,do 之前的括號不能省略;
4、command1 表示字符串或變量,command2 表示字符串、變量或命令語句;
現在,你可能已經會寫一個簡單的for 語句了,比如:
[code1]
@echo off
for %%I in (bbs.bathome.cn) do echo %%I
pause
保存爲批處理文件並執行,將會在彈出的批處理窗口中看到這樣的信息:
[result1]
bbs.bathome.cn
請按任意鍵繼續...
很快地,你會覺得這個for 語句是如此的簡單,簡單到你絲毫感受不出它的
強大:這個for 語句,和我直接用echo 語句沒什麼兩樣啊!
是的,演示代碼永遠都只是演示而已,就像大多數高級語言的教科書一樣,
在引導新手學習的時候,基本上都是千篇一律地告訴大家如何編寫一個能顯示
hello world! 的窗口,從這些演示代碼中,你看不到它們具有多少實用性,你只
是感到有點好奇:咦,居然彈出了一個窗口?片刻之後,你就會覺得索然無味。
那好吧,爲了讓大家對 for 更加感興趣,我們先來分析一下 for 語句的一些
注意事項,之後,再讓大家看看更爲強大的for 語句實例。
1、for 語句的形式變量 I,可以換成 26 個字母中的任意一個,這些字母會
區分大小寫,也就是說,%%I 和%%i 會被認爲不是同一個變量;形式變量 I
還可以換成其他的字符,但是,爲了不與批處理中的%0~%9 這10 個形式變量
發生衝突,請不要隨意把%%I 替換爲%%0 ~%%9 中的任意一個;
2、in 和do 之間的command1 表示的字符串或變量可以是一個,也可以是
多個,每一個字符串或變量,我們稱之爲一個元素,每個元素之間,用空格鍵、
跳格鍵、逗號、分號或等號分隔;
3、for 語句依次提取 command1 中的每一個元素,把它的值賦予形式變量
I,帶到do 後的command2 中參與命令的執行;並且每次只提取一個元素,然後
執行一次 do 後的命令語句,而無論這個元素是否被帶到 command2 中參與了
command2 的運行;當執行完一次do 後的語句之後,再提取command1 中的下
一個元素,再執行一次 command2,如此循環,直到 command1 中的所有元素
都已經被提取完畢,該for 語句才宣告執行結束;
其中,第3 點是最爲關鍵的,它描述了for 語句的執行過程,是for 語句的
精髓所在,大家一定要牢記這一條,才能深刻理解更爲複雜的for 流程。
有了以上的基礎,我們再來看一個例子,這個例子修改了code1 的部分內容,
結果將大不一樣:
[code2]
@echo off
for %%I in (bbs,bathome,cn) do echo %%I
pause
和code1 的執行結果相比,code2 的執行結果發生瞭如下變化:
1、顯示結果分成了3 行(不算最後一行中文提示);
2、每一行都從逗號處被切分;
如果把 bbs.bathome.cn 這個字符串中的點號換爲空格、跳格或等號,執行
結果將和code2 的執行結果別無二致。
現在,我們來分析一下code2 代碼中for 語句的執行過程:
首先,for 語句以逗號爲分隔符,把 bbs,bathome,cn 這個字符串切分成三個
元素:bbs、bathome 和cn,由此決定了do 後的語句將會被執行3 次;
然後,第一次執行過程是這樣的:先把 bbs 這個字符串作爲形式變量 I 的
值,帶入do 後的語句中加以執行,也就是執行 echo %%I 語句,此時的I 值爲
bbs,因此,第一次執行的結果,將會在屏幕上顯示 bbs 這個字符串;第二次執
行和第一次執行的過程是一樣的,只不過此時I 的值已經被替換爲command1 中
的第二個元素了,也就是 bathome 這個字符串;如此循環,當第三次echo 執行
完畢之後,整條for 語句纔算執行完畢,此時,將執行下一條語句,也就是pause
命令。
其實,這個例子只比上一個例子多了一點花樣,有趣了那麼一點點:一條for
語句的執行結果居然被分成了3 行!
爲了讓大家見識一下for 的真正威力,本人絞盡腦汁,翻帖無數,不得要領,
萬般無奈之下,只好亮出了塵封在箱底多年的一段代碼:檢測當前硬盤都有哪些
分區^_^
[code3]
@echo off
set str=c d e f g h i j k l m n o p q r s t u v w x y z
echo 當前硬盤的分區有:
for %%i in (%str%) do if exist %%i: echo %%i:
pause
這段代碼能檢測硬盤都有哪些分區,包括 U 盤和移動硬盤的分區,但是,
當光驅中有盤的時候,也會被列出來,這是本代碼的一個缺憾,在以後的講解中,
我將向大家講述如何消除這個瑕疵,敬請關注本系列的後續章節。
高級應用:
想知道當前目錄下都有哪些文件嗎?請用下面的代碼:
@echo off
for %%i in (*.*) do echo "%%i"
pause
想列出當前目錄下所有的文本文件嗎?請用下面的代碼:
@echo off
for %%i in (*.txt) do echo "%%i"
pause
想列出只用兩個字符作爲文件名的文本文件嗎?請用下面的代碼:
@echo off
for %%i in (??.txt) do echo "%%i"
pause
題外話:
1、列出當前目錄下各種文件的方法,最簡單的還是用dir 命令,但是,從以
上代碼中,各位可以加深對for 語句執行流程的理解(用到了通配符*和?);
2、注意:以上代碼不能列出含有隱藏或系統屬性的文件;
練習:用for 語句建立test1.txt、test2.txt 和test3.txt 三個文本文件。
更全面的練習請看這個帖子:for 語句從入門到精通配套練習題
http://bbs.bathome.cn/thread-2336-1-1.html
三、文本解析顯神威:for /f 用法詳解
for /f 是個十分強大的傢伙。
如果說,for 語句是批處理中最強大的語句的話,那麼,for /f 就是精華中的
精華。
for /f 的強大,和它擁有衆多的開關密切相關。因爲開關衆多,所以用法復
雜,本章將分成若干小節,爲大家逐一介紹強大的 for /f 語句。
(一) 爲解析文本而生:f or /f 的基本用法
所有的對象,無論是文件、窗體、還是控件,在所有的非機器語言看來,無
外乎都是形如"c:\test.txt"、"CWnd"之類的文本信息;而所有的對象,具體的如ini
文件中的某條配置信息、註冊表中的某個鍵值、數據庫中的某條記錄„„都只有
轉化爲具有一定格式的文本信息,方可被代碼識別、操控。可以說,編程的很大
一部分工作,都是在絞盡腦汁想方設法如何提取這些文本信息。
而提取文本信息,則是for /f 的拿手好戲:讀取文件內容;提取某幾行字符;
截取某個字符片段;對提取到的內容再切分、打亂、雜糅„„只要你所能想到的
花樣,for /f 都會想方設法幫你辦到,因爲,for /f 就是被設計成專門用於解析文
本的。
先來看個例子。
假如有個文本文件test.txt,內容如下:
[txt1]
論壇的目標是:不求最大,但求最好,做最實用的批處理論壇。
論壇地址:bbs.bathome.cn。
這裏是:新手晉級的福地,高手論劍的天堂。
那麼,將如下代碼保存爲test.cmd,並放在test.txt 同一目錄下運行,將會在
屏幕上原樣顯示test.txt 的內容:
[code4]
@echo off
for /f %%i in (test.txt) do echo %%i
pause
這段代碼,主要是讓你樹立這樣一種觀念:讀取文本文件的內容,請使用 for
/f 語句!
進階話題:for /f 語句是把整個test.txt 一次性顯示出來的?
在這段代碼中,雖然執行結果是把 test.txt 中的所有內容都顯示出來了,貌
似 for /f 語句是把整個test.txt 一次性顯示到屏幕上,實際上並非如此。
無論for 語句做何種變化,它的執行過程仍然遵循基本的for 流程:依次處
理每個元素,直到所有的元素都被處理爲止。只不過在for /f 語句中,這裏的元
素是指文件中的每一行,也就是說,for /f 語句是以行爲單位處理文本文件的。
這是一條極爲重要的規則,在上一章中也強調過它的重要性,希望在接下來的學
習過程中,你能時刻牢記這一原則,那麼,很多問題將會迎刃而解。以下是驗證
這一說法的演示代碼(在[code4]的基礎上添加了&pause 語句):
[code5]
@echo off
for /f %%i in (test.txt) do echo %%i&pause
pause
(二) 切分字符串的利器:delims=
也許你對[code4]這段代碼不屑一顧:不就是把test.txt 的內容顯示出來了麼?
好像用處不大啊。
好吧,我們來玩個魔術。
還是[txt1]這段文本,把[code4]改造一下:
[code6]
@echo off
for /f "delims=," %%i in (test.txt) do echo %%i
pause
再次運行test.cmd,看到什麼變化了嗎?!
[result2]
論壇的目標是:不求最大
論壇地址:bbs.bathome.cn。
這裏是:新手晉級的福地
請按任意鍵繼續...
結果,你驚奇地發現,每行第一個逗號之後的所有內容都不見了(如果有不
存在逗號的行,則保留原樣),也就說,你成功地提取到了每行第一個逗號之前
的所有內容!
試想一下,這段代碼會有什麼用呢?
如果別人給了你一個軟件清單,每行都是"英文軟件名(逗號)中文軟件名"
的格式,而你卻只想保留英文名的時候,這段代碼將是多麼有用啊!再假設,有
這麼一個IP 文件,第一列是數字格式的IP 地址,第二列是具體的空間地址,列
與列之間用逗號分隔,而你想提取其中數字格式的 IP,呵呵,我不說你也知道
該怎麼辦了吧?
要是文本內容不是以逗號分隔,而是以其他符號分隔,那麼,把"delims=,"
的逗號換成相應的符號就可以了。
在這裏,我們引入了一個新的開關:"delims=,",它的含義是:以逗號作爲
被處理的字符串的分隔符號。
在批處理中,指定分隔符號的方法是:添加一個形如 "delims=符號列表" 的
開關,這樣,被處理的每行字符串都會被符號列表中羅列出來的符號切分開來。
需要注意的是:如果沒有指定"delims=符號列表"這個開關,那麼,for /f 語
句默認以空格鍵或跳格鍵作爲分隔符號。請把[txt1]中不同位置上的標點符號改
爲空格或跳格,再運行[code4]試試。
進階話題:如果我要指定的符號不止一個,該怎麼辦?
在上面的講解中,我提到了指定分隔符號的方法:添加一個形如"delims=符
號列表"的開關。不知道你注意到沒有,我的說法是"符號列表"而非"符號",這是
大有講究的,因爲,你可以一次性指定多個分隔符號!
還是以[txt1]爲例,把[code6]再改造一下:
[code7]
@echo off
for /f "delims=.," %%i in (test.txt) do echo %%i
pause
結果顯示:
[result3]
論壇的目標是:不求最大
論壇地址:bbs
這裏是:新手晉級的福地
請按任意鍵繼續...
這樣,第一個點號或第一個逗號之前的內容都被提取出來了。
[code7]的執行過程是:逐行讀取 test.txt 中的內容,以點號和逗號切分每一
行的內容(不存在點號和逗號的行,則不再切分,爲了描述的方便,我們把被點
號或逗號切分的一個一個的字符串片段,稱之爲節),然後,for /f 會提取第一節
的內容作爲最終結果,顯示在屏幕上。需要注意的是,在這裏,所有行的字符串
被切分成了兩個以上的節,但是,[code7]的代碼只會提取第一節字符串的內容,
因爲 for /f 語句默認只提取第一節的符串。
(三) 定點提取:tokens=
上一節在講解 delims= 的時候,我一再強調 for /f 默認只能提取到第一節
的內容,現在我們來思考一個問題:如果我要提取的內容不在第一節上,那怎麼
辦?
這回,就該輪到 tokens= 出馬了。
tokens= 後面一般跟的是數字,如 tokens=2,也可以跟多個,但是每個數字
之間用逗號分隔,如 tokens=3,5,8,它們的含義分別是:提取第2 節字符串、提
取第3、第5 和第8 節字符串。注意,這裏所說的“節”,是由 delims= 這一開
關劃分的,它的內容並不是一成不變的。
下面來看一個例子:
[txt2]
尺有所短,寸有所長,學好批處理沒商量,考慮問題複雜化,解決問題簡潔化。
對[txt2]這段文本,假設它們保存在文件 test.txt 中,如果我想提取“學好批
處理沒商量”這句話,該如何寫代碼呢?
我們稍微觀察一下[txt2]就會發現,如果以逗號作爲切分符號,就正好可以
把“學好批處理沒商量”化爲單獨的一“節”,結合上一節的講解,我們知道,
"delims=," 這個開關是不可缺少的,而要提取的內容在以逗號切分的第3 節上,
那麼,tokens= 後面的數字就應該是3 了,最終的代碼如下:
[code8]
@echo off
for /f "delims=, tokens=3" %%i in (test.txt) do echo %%i
pause
如果我們現在要提取的不只一個“節”,而是多個,那又怎麼辦呢?比如,
要提取以逗號切分的第2 節和第5 節字符串,是寫成這樣嗎?
[code9]
@echo off
for /f "delims=, tokens=2,5" %%i in (test.txt) do echo %%i
pause
運行批處理後發現,執行結果只顯示了第2 節的內容。
原來,echo 後面的 %%i 只接收到了 tokens=2,5 中第一個數值2 所代表的
那個字符串,而第二個數值5 所代表的字符串因爲沒有變量來接收,所以就無法
在執行結果中顯示出來了。
那麼,要如何接收 tokens= 後面多個數值所指代的內容呢?
for /f 語句對這種情況做如下規定:
如果 tokens= 後面指定了多個數字,如果形式變量爲%%i,那麼,第一個
數字指代的內容用第一個形式變量%%i 來接收,第二個數字指代的內容用第二
個形式變量%%j 來接收,第三個數字指代的內容用第三個形式變量%%k 來接
收„„第N 個數字指代的內容用第N 個形式變量來接收,其中,形式變量遵循
字母的排序,第 N 個形式變量具體是什麼符號,由第一個形式變量來決定:如
果第一個形式變量是%%i,那麼,第二個形式變量就是%%j;如果第一個形式
變量用的是%%x,那麼,第二個形式變量就是%%y。
現在回頭去看[code9],你應該知道如何修改才能滿足題目的要求了吧?修改
12
結果如下:
[code10]
@echo off
for /f "delims=, tokens=2,5" %%i in (test.txt) do echo %%i %%j
pause
如果有這樣一個要求:顯示[txt2]中的內容,但是逗號要替換成空格,如何
編寫代碼?
結合上面所學的內容,稍加思索,你可能很快就得出了答案:
[code11]
@echo off
for /f "delims= , tokens=1,2,3,4,5" %%i in (test.txt) do
echo %%i %%j %%k %%l %%m
pause
寫完之後,你可能意識到這樣一個問題:假如要提取的“節”數不是5,而
是10,或者20,或者更多,難道我也得從1 寫到10、20 或者更多嗎?有沒有更
簡潔的寫法呢?
答案是有的,那就是:如果要提取的內容是連續的多“節”的話,那麼,連
續的數字可以只寫最小值和最大值,中間用短橫連接起來即可,比如
tokens=1,2,3,4,5 可以簡寫爲 tokens=1-5 。
還可以把這個表達式寫得更復雜一點:tokens=1,2-5 ,tokens=1-3,4,5 ,
tokens=1-4,5„„怎麼方便就怎麼寫吧。
大家可能還看到一種比較怪異的寫法:
[code12]
@echo off
for /f "delims=, tokens=1,*" %%i in (test.txt) do echo %%i %%j
pause
結果,第一個逗號不見了,取代它的是一個空格符號,其餘部分保持不變。
其中奧妙就在這個星號上面。
tokens=後面所接的星號具備這樣的功能:字符串從左往右被切分成緊跟在*
之前的數值所表示的節數之後,字符串的其餘部分保持不變,整體被*所表示的
一個變量接收。
理論講解是比較枯燥的,特別是爲了嚴密起見,還使用了很多限定性的修飾
詞,導致句子很長,增加了理解的難度,我們還是結合[code12]來講解一下吧。
[txt2] 的內容被切分,切分符號爲逗號,當切分完第一節之後,切分動作不
再繼續下去,因爲 tokens=1,* 中,星號前面緊跟的是數字 1;第一節字符串被
切分完之後,其餘部分字符串不做任何切分,整體作爲第二節字符串,這樣,[txt2]
就被切分成了兩節,分別被變量%%i 和變量%%j 接收。
以上幾種切分方式可以結合在一起使用。不知道下面這段代碼的含義你是否
看得懂,如果看不懂的話,那就運行一下代碼,然後反覆揣摩,你一定會更加深
刻地理解本節所講解的內容的:
[code13]
@echo off
for /f "delims=, tokens=1,3-4,*" %%i in (test.txt) do echo %%i %%j %%k %%l
pause
(四) 跳過無關內容,直奔主題:skip=n
很多時候,有用的信息並不是貫穿文本內容的始終,而是位於第 N 行之後
的行內,爲了提高文本處理的效率,或者不受多餘信息的干擾,for /f 允許你跳
過這些無用的行,直接從第N+1 行開始處理,這個時候,就需要使用參數 skip=n,
其中,n 是一個正整數,表示要跳過的行數。例如:
[code14]
@echo off
for /f "skip=2" %%i in (test.txt) do echo %%i
pause
這段代碼將跳過頭兩行內容,從第3 行起顯示test.txt 中的信息。
(五) 忽略以指定字符打頭的行:eol=
在cmd 窗口中敲入:for /?,相關的解釋爲:
[quote]
eol=c - 指一個行註釋字符的結尾(就一個)
[/quote]
[quote]
FOR /F "eol=; tokens=2,3* delims=, " %i in (myfile.txt) do @echo %i %j %k
會分析 myfile.txt 中的每一行,忽略以分號打頭的那些行„„
[/quote]
第一條解釋狗屁不通,頗爲費解:行註釋字符的結尾是什麼意思?“(就一
個)”怎麼回事?結合第二條解釋,才知道eol 有忽略指定行的功能。但是,這兩
條解釋是互相矛盾的:到底是忽略以指定字符打頭的行,還是忽略以指定字符結
尾的行?
實踐是檢驗真理的唯一標準,還是用代碼來檢驗一下eol 的作用吧:
[code15]
@echo off
for /f "eol=;" %%i in (test.txt) do echo %%i
pause
結果,那些以分號打頭的行沒有顯示出來。
由此可見,第二條解釋是正確的,eol= 的準確含義是:忽略以指定字符打
頭的行。而第一條的“結尾”純屬微軟在信口開河。
那麼,“(就一個)”又作何解釋呢?
試試這個代碼:
[code16]
@echo off
for /f "eol=,;" %%i in (test.txt) do echo %%i
pause
此時,屏幕上出現 此時不應有 ;"。 的報錯信息。可見,在指定字符的時
候,只能指定1 個——在很多時候,我對這樣的設計頗有微詞而又無可奈何:爲
什麼只能指定1 個而不是多個?要忽略多個還得又是 if 又是 findstr 加管道來多
次過濾,那效率實在太低下了——能用到的功能基本上都提供,但是卻又做不到
更好,批處理,你的功能爲什麼那麼弱?
不知道大家注意到沒有,如果 test.txt 中有以分號打頭的行,那麼,這些行
在代碼[code14]的執行結果中將憑空消失。
原來,for /f 語句是默認忽略以分號打頭的行內容的,正如它默認以空格鍵
或跳格鍵作爲字符串的切分字符一樣。
很多時候,我們可以充分利用這個特點,比如,在設計即將用for 讀取的配
置文件的時候,可以在註釋文字的行首加上分號,例如在編寫病毒文件查殺代碼
的時候,可以通過 for 語句來讀取病毒文件列表,那麼,病毒文件列表.ini 這個
配置文件可以這樣寫:
;以下是常見的病毒文件,請見一個殺一個^_^
;copyleft:沒有
qq.exe
msn.exe
iexplore.exe
如果要取消這個默認設置,可選擇的辦法是:
1、爲eol=指定另外一個字符;
2、使用 for /f "eol=" 語句,也就是說,強制指定字符爲空,就像對付delims=
一樣。
(六)如何決定該使用 f or /f 的哪種句式?(兼談
usebackq 的使用)
for /f %%i in („„) do („„) 語句有好幾種變形語句,不同之處在於第一個
括號裏的內容:有的是用單引號括起來,有的是用雙引號包住,有的不用任何符
號包裹,具體格式爲:
1、for /f %%i in (文件名) do („„)
2、for /f %%i in ('命令語句') do („„)
3、for /f %%i in ("字符串") do („„)
看到這裏,我想很多人可能已經開始犯了迷糊了:如果要解決一個具體問題,
面對這麼多的選擇,如何決定該使用哪一條呢?
實際上,當我在上面羅列這些語句的時候,已經有所提示了,不知道你是否
注意到了。
如果你一時無法參透其中奧妙,那也無妨,請聽我一一道來便是。
1、當你希望讀取文本文件中的內容的話,第一個括號中不用任何符號包裹,
應該使用的是第1 條語句;例如:你想顯示test.txt 中的內容,那麼,就使用 for
/f %%i in (test.txt) do echo %%i;
2、當你讀取的是命令語句執行結果中的內容的話,第一個括號中的命令語
句必須使用單引號包裹,應該使用的是第2 條語句;例如:你想顯示當前目錄下
文件名中含有test 字符串的文本文件的時候,應該使用 for /f %%i in ('dir /a-d
/b *test*.txt') do echo %%i 這樣的語句;
3、當你要處理的是一個字符串的時候,第一個括號中的內容必須用雙引號
括起來,應該是用的是第 3 條語句;例如:當你想把 bbs.bathome.cn 這串字符
中的點號換爲短橫線並顯示出來的話,可以使用 for /f "delims=.
tokens=1-3" %%i in ("bbs.bathome.cn") do echo %%i-%%j-%%k 這樣的語
句。
很顯然,第一個括號裏是否需要用符號包裹起來,以及使用什麼樣的符號包
裹,取決於要處理的對象屬於什麼類型:如果是文件,則無需包裹;如果是命令
語句,則用單引號包裹;如果是字符串,則使用雙引號括起來。
當然,事情並不是絕對如此,如果細心的你想到了批處理中難纏的特殊字符,
你肯定會頭大如鬥。
或許你頭腦中靈光一閃,已經想到了一個十分頭痛的問題:在第1 條語句中,
如果文件名中含有空格或&,該怎麼辦?
照舊嗎?
拿個叫 test 1.txt 的文件來試試。
你很快寫好了代碼,新建文件-->碼字-->保存爲批處理,前後費時不到 1 分
鍾:
[code17]
@echo off
for /f %%i in (test 1.txt) do echo %%i
pause
你興沖沖地雙擊批處理,運行後,屏幕上出現了可恥的報錯信息:系統找不
到文件 test 。
當你把 test 1.txt 換成 test&1.txt 後,更怪異的事情發生了:CMD 窗口在你
眼前一閃而過,然後,優雅地消失了。
你可能覺得自己的代碼寫錯了某些符號,你再仔細的檢查了一次,確認沒有
筆誤,然後,你再次雙擊批處理,結果問題照舊;你開始懷疑其他程序對它可能
有影響,於是關掉其他窗口,再運行了一次,問題依舊;你不服氣地連續運行了
好幾次,還是同樣的結果。
怪哉!
你一拍大腿,猛然想起了一件事:當路徑中含有特殊字符的時候,應該使用
引號把路徑括起來。對,就是它了!
但是,當你把代碼寫出來之後,你很快就焉了:for /f %%i in ("test 1.txt") do
echo %%i,這不就是上面提到的第3 條 for /f 命令的格式嗎?批處理會把 test
1.txt 這個文件名識別爲字符串啊!
你百無聊賴地在CMD 窗口中輸入 for /? ,並重重地敲下了回車,漫無目的
地在幫助信息中尋找,希望能找到點什麼。
結果還真讓你到了點什麼。
你看到了這樣的描述:
usebackq - 指定新語法已在下類情況中使用:
在作爲命令執行一個後引號的字符串並且一個單
引號字符爲文字字符串命令並允許在 filenameset
中使用雙引號擴起文件名稱。
但是,通讀一遍之後,你卻如墜五里霧中,不知所云。
還好,下面有個例子,並配有簡單的說明:
FOR /F "usebackq delims==" %i IN (`set`) DO @echo %i
會枚舉當前環境中的環境變量名稱。
你仔細對比了 for /f 語句使用usebackq 和不使用usebackq 時在寫法上的差
別,很快就找到了答案:當使用了 usebackq 之後,如果第一個括號中是一條命
令語句,那麼,就要把單引號'改成後引號`(鍵盤左上角esc 鍵下面的那個按鍵,
與~在同一鍵位上)。
回過頭去再看那段關於 usebackq 的描述,字斟句酌,反覆揣摩,終於被你
破譯了天機:usebackq 是一個增強型參數,當使用了這個參數之後,原來的for
語句中第一個括號內的寫法要做如下變動:如果第一個括號裏的對象是一條命
令語句的話,原來的單引號'要改爲後引號`;如果第一個括號裏的對象是字符串
的話,原來的雙引號"要改爲單引號';如果第一個括號裏的對象是文件名的話,
要用雙引號"括起來。
驗證一下,把[code17]改寫成如下代碼:
[code18]
@echo off
for /f "usebackq" %%i in ("test 1.txt") do echo %%i
pause
測試通過! 此時,你很可能會仰天長嘆:Shit,微軟這該死的機器翻譯!
至於把[code17]代碼中的空格換成&後,CMD 窗口會直接退出,那是因爲&
是複合語句的連接符,CMD 在預處理的時候,會優先把&前後兩部分作爲兩條
語句來解析,而不是大家想象中的一條完整的for 語句,從而產生了嚴重的語法
錯誤。因爲牽涉到預處理機制問題,不屬於本節要討論的內容,在此不做詳細講
解。
這個時候,我們會吃驚地發現,區區一條for 語句,竟然有多達6 種句型:
1、for /f %%i in (文件名) do („„)
2、for /f %%i in ('命令語句') do („„)
3、for /f %%i in ("字符串") do („„)
4、for /f "usebackq" %%i in ("文件名") do („„)
5、for /f "usebackq" %%i in (`命令語句`) do („„)
6、for /f "usebackq" %%i in ('字符串') do („„)
其中,4、5、6 由1、2、3 發展而來,他們有這樣的對應關係:1-->4、2-->5、
3-->6。
好在後3 種情形並不常用,所以,牢牢掌握好前三種句型的適用情形就可以
了,否則,要在這麼多句型中確定選擇哪一條語句來使用,還真有點讓人頭腦發
懵。
至於 for /f 爲什麼要增加usebacq 參數,我只爲第4 條語句找到了合理的解
釋:爲了兼容文件名中所帶的空格或&。它在第 5、6 條語句中爲什麼還有存在
的必要,我也不是很明白,這有待於各位去慢慢發現。
(七)變量延遲詳解
變量延遲在 for 語句中起着至關重要的作用,不只是在 for 語句中,在其他
的複合語句中,它也在幕後默默地工作着,爲了突出它的重要性,本節內容在單
獨的樓層中發出來,希望引起大家的重視。
對於批處理新手而言,“變量延遲”這個概念很可能聞所未聞,但是,它卻
像一堵橫亙在你前進道路上的無形高牆,你感受不到它的存在,但當你試圖往前
衝時,它會把你狠狠地彈回來,讓你無法逾越、無功而返;而一旦找到了越過它
的方法,你就會發現,在for 的世界裏,前面已經是一片坦途,而你對批處理的
理解,又上升到了一個新的境界。
例如,你編寫了這樣一個代碼:
[code19]
@echo off
set num=0&&echo %num%
pause
你的本意是想對變量 num 賦值之後,再把這個值顯示出來,結果,顯示出
來的並不是0,而是顯示:ECHO 處於關閉狀態。
之所以會出錯,是因爲“變量延遲”這個傢伙在作怪。
在講解變量延遲之前,我們需要了解一下批處理的執行過程,它將有助於我
們深入理解變量延遲。
批處理的執行過程是怎樣的呢?
“自上而下,逐條執行”,我想,這個經典的說法大家都已經耳熟能詳了,
沒事的時候倒着念,也還別有一番古韻呢^_^,但是,我想問大家的是,大家真
的深刻地理解了這句話的含義了嗎?
“自上而下”,這一條和我們本節的講解關係不大,暫時略過不說,後一條,
“逐條執行”和變量延遲有着莫大的干係,它是我們本節要關注的重點。
很多人往往認爲一行代碼就是一條語句,從而把“逐條執行”與“逐行執行”
等同起來,這就大錯特錯了。
莫非“逐條執行”裏暗藏着玄機?
正是如此。
“逐條”並不等同於“逐行”。這個“條”,是“一條完整的語句”的意思,
並不是指“一行代碼”。在批處理中,是不是一條完整的語句,並不是以行來論
的,而是要看它的作用範圍。
什麼樣的語句纔算“一條完整的語句”呢?
1、在複合語句中,整個複合語句是一條完整的語句,而無論這個複合語句
佔用了多少行的位置。常見的複合語句有:for 語句、if„„else 語句、用連接符
&、||和&&連接的語句,用管道符號|連接的語句,以及用括號括起來的、由多條
語句組合而成的語句塊;
2、在非複合語句中,如果該語句佔據了一行的位置,則該行代碼爲一條完
整的語句。
例如:
[code20]
@echo off
set num=0
for /f %%i in ('dir /a-d /b *.exe') do (
set /a num+=1
echo num 當前的值是 %num%
)
echo 當前目錄下共有 %num% 個exe 文件
dir /a-d /b *.txt|findstr "test">nul&&(
echo 存在含有 test 字符串的文本本件
)||echo 不存在含有 test 字符串的文本文件
if exist test.ini (
echo 存在 test.ini 文件
) else echo 不存在 test.ini 文件
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 語句。
在這裏,我之所以要花這麼長的篇幅來說明一行代碼並不見得就是一條語
句,是因爲批處理的執行特點是“逐條”執行而不是“逐行”執行,澄清了這個
誤解,將會更加理解批處理的預處理機制。
在代碼“逐條”執行的過程中,cmd.exe 這個批處理解釋器會對每條語句做
一些預處理工作,這就是批處理中大名鼎鼎的“預處理機制”。預處理的大致情
形是這樣的:首先,把一條完整的語句讀入內存中(不管這條語句有多少行,
它們都會被一起讀入),然後,識別出哪些部分是命令關鍵字,哪些是開關、哪
些是參數,哪些是變量引用„„如果代碼語法有誤,則給出錯誤提示或退出批
處理環境;如果順利通過,接下來,就把該條語句中所有被引用的變量及變量
兩邊的百分號對,用這條語句被讀入內存之就已經賦予該變量的具體值來替
換„„當所有的預處理工作完成之後,批處理纔會執行每條完整語句內部每個
命令的原有功能。也就是說,如果命令語句中含有變量引用(變量及緊鄰它左
右的百分號對),並且某個變量的值在命令的執行過程中被改變了,即使該條語
句內部的其他地方也用到了這個變量,也不會用最新的值去替換它們,因爲某
條語句在被預處理的時候,所有的變量引用都已經被替換成字符串常量了,變
量值在複合語句內部被改變,不會影響到語句內部的其他任何地方。
順便說一下,運行代碼[code20]之後,將在屏幕上顯示當前目錄下有多少個
exe 文件,是否存在含有 test 字符串的文本文件,以及是否存在 test.ini 這個文
件等信息。讓很多人百思不得其解的是:如果當前目錄下存在exe 文件,那麼,
有多少個exe 文件,屏幕上就會提示多少次 "num 當前的值是 0" ,而不是顯示
1 到N(N 是exe 文件的個數)。
結合上面兩個例子,我們再來分析一下,爲什麼這兩段代碼的執行結果和我
們的期望有一些差距。
在[code19]中,set num=0&&echo %num%是一條複合語句,它的含義是:把
0 賦予變量num,成功後,顯示變量num 的值。
雖然是在變量num 被賦值成功後才顯示變量num 的值,但是,因爲這是一
條複合語句,在預處理的時候,&&後的%num%只能被set 語句之前的語句賦予
變量num 的具體值來替換,而不能被複合語句內部、&&之前的set 語句對num
所賦予的值來替換,可見,此 num 非彼 num。可是,在這條複合語句之前,我
們並沒有對變量num 賦值,所以,&&之後的%num%是空值,相當於在&&之後
只執行了 echo 這一命令,所以,會顯示 echo 命令的當前狀態,而不是顯示變
量num 的值(雖然該變量的值被set 語句改變了)。
在[code20]中,for 語句的含義是:列舉當前目錄下的exe 文件,每發現一個
exe 文件,變量num 的值就累加1,並顯示變量num 的值。
看了對[code19]的分析之後,再來分析[code20]就不再那麼困難了:第3、4、
5 行上的代碼共同構成了一條完整的 for 語句,而語句"echo num 當前的值
是 %num%"與"set /a num+=1"同處複合語句for 的內部,那麼,第4 行上set 改
變了num 的值之後,並不能對第5 行上的變量num 有任何影響,因爲在預處理
階段,第5 行上的變量引用%num%已經被在for 之前就賦予變量num 的具體值
替換掉了,它被替換成了0(是被第2 行上的set 語句賦予的)。
如果想讓代碼[code19]的執行結果中顯示&&之前賦予 num 的值,讓代碼
[code20]在列舉exe 文件的時候,從1 到N 地顯示exe 文件的數量,那又該怎麼
辦呢?
對代碼[code19],可以把用&&連接複合語句拆分爲兩條單獨的語句,寫成:
@echo off
set num=0
echo %num%
pause
但是,這不是我們這次想要的結果。
對這兩段代碼都適用的辦法是:使用變量延遲擴展語句,讓變量的擴展行爲
延遲一下,從而獲取我們想要的值。
在這裏,我們先來充下電,看看“變量擴展”有是怎麼一回事。
用 CN-DOS 裏批處理達人 willsort 的原話,那就是:“在許多可見的官方文
檔中,均將使用一對百分號閉合環境變量以完成對其值的替換行爲稱之爲“擴展
(expansion)”,這其實是一個第一方的概念,是從命令解釋器的角度進行稱謂的,
而從我們使用者的角度來看,則可以將它看作是引用(Reference)、調用(Call)
或者獲取( Get )。”( 見 : 什 麼 情 況 下 該 使 用 變 量 延 遲 ?
http://www.cn-dos.net/forum/viewthread.php?tid=20733)說得直白一點,所謂的“變
量擴展”,實際上就是很簡單的這麼一件事情:用具體的值去替換被引用的變量
及緊貼在它左右的那對百分號。
既然只要延遲變量的擴展行爲,就可以獲得我們想要的結果,那麼,具體的
做法又是怎樣的呢?
一般說來,延遲變量的擴展行爲,可以有如下選擇:
1、在適當位置使用 setlocal enabledelayedexpansion 語句;
2、在適當的位置使用 call 語句。
使用 setlocal enabledelayedexpansion 語句,那麼,[code19]和[code20]可以
分別修改爲:
@echo off
setlocal enabledelayedexpansion
set num=0&&echo !num!
pause
@echo off
set num=0
setlocal enabledelayedexpansion
for /f %%i in ('dir /a-d /b *.exe') do (
set /a num+=1
echo num 當前的值是 !num!
)
echo 當前目錄下共有 %num% 個exe 文件
dir /a-d /b *.txt|findstr "test">nul&&(
echo 存在含有 test 字符串的文本本件
)||echo 不存在含有 test 字符串的文本文件
if exist test.ini (
echo 存在 test.ini 文件
) else 不存在 test.ini 文件
pause
使用第call 語句,那麼,[code19]和[code20]可以分別修改爲:
@echo off
set num=0&&call echo %%num%%
pause
@echo off
set num=0
for /f %%i in ('dir /a-d /b *.exe') do (
set /a num+=1
call echo num 當前的值是 %%num%%
)
echo 當前目錄下共有 %num% 個exe 文件
dir /a-d /b *.txt|findstr "test">nul&&(
echo 存在含有 test 字符串的文本本件
)||echo 不存在含有 test 字符串的文本文件
if exist test.ini (
echo 存在 test.ini 文件
) else 不存在 test.ini 文件
pause
由此可見,如果使用 setlocal enabledelayedexpansion 語句來延遲變量,就要
把原本使用百分號對閉合的變量引用改爲使用感嘆號對來閉合;如果使用 call
語句,就要在原來命令的前部加上 call 命令,並把變量引用的單層百分號對改
爲雙層。 其中,因爲 call 語句使用的是雙層百分號對,容易使人犯迷糊,所以
用得較少,常用的是使用 setlocal enabledelayedexpansion 語句(set 是設置的意
思,local 是本地的意思,enable 是能夠的意思,delayed 是延遲的意思,expansion
是擴展的意思,合起來,就是:讓變量成爲局部變量,並延遲它的擴展行爲)。
通過上面的分析,我們可以知道:
1、爲什麼要使用變量延遲?因爲要讓複合語句內部的變量實時感知到變量
值的變化。
2、在哪些場合需要使用變量延遲語句?在複合語句內部,如果某個變量的
值發生了改變,並且改變後的值需要在複合語句內部的其他地方被用到,那麼,
就需要使用變量延遲語句。而複合語句有:for 語句、if„„else 語句、用連接符
&、||和&&連接的語句、用管道符號|連接的語句,以及用括號括起來的、由多
條語句組合而成的語句塊。最常見的場合,則是for 語句和if„„else 語句。
3、怎樣使用變量延遲?
方法有兩種:
① 使用 setlocal enabledelayedexpansion 語句:在獲取變化的變量值語句
之前使用setlocal enabledelayedexpansion,並把原本使用百分號對閉合的變量引
用改爲使用感嘆號對來閉合;
② 使用 call 語句:在原來命令的前部加上 call 命令,並把變量引用的單
層百分號對改爲雙層。
“變量延遲”是批處理中一個十分重要的機制,它因預處理機制而生,用於
複合語句,特別是大量使用於強大的for 語句中。只有熟練地使用這一機制,才
能在for 的世界中如魚得水,讓自己的批處理水平更上一層樓。很多時候,對for
的處理機制,我們一直是霧裏看花,即使偶有所得,也只是只可意會難以言傳。
希望大家反覆揣摩,多加練習,很多細節上的經驗,是隻有通過大量的摸索才能
得到的。Good Luck!
本節內容在原理上參考了這篇文章:什麼情況下該使用變量延遲?
http://www.cn-dos.net/forum/viewthread.php?tid=20733 ,在本論壇中的地址是:
http://bbs.bathome.cn/viewthread.php?tid=2899
特別鳴謝:willsort。
四、翻箱倒櫃遍歷文件夾:for /r
(一)f or /r 的作用及用法
按照幫助信息裏文縐縐的說法,for /r 的作用是“遞歸”,我們換一個通俗一
點的,叫“遍歷文件夾”。
更詳細的解釋就是:在下面的語句中,如果“元素集合”中只是一個點號,
那麼,這條語句的作用就是:列舉“目錄”及其之下的所有子目錄,對這些文件
夾都執行“命令語句集合”中的命令語句。其作用與嵌套進 for /f 複合語句的 "dir
/ad /b /s 路徑" 功能類似。如果省略了“目錄”,將在當前目錄下執行前面描述的
操作。
[quote]
for /r 目錄 %%i in (元素集合) do 命令語句集合
[/quote]
先來個代碼增強一下印象:
[code21]
@echo off
for /r d:\test %%i in (.) do echo %%i
pause
執行的結果如下所示:
[quote]
d:\test\.
d:\test\1\.
d:\test\2\.
d:\test\3\.
[/quote]
效果就是顯示 d:\test 目錄及其之下是所有子目錄的路徑,其效果與 dir /ad
/b /s d:\test 類似。若要說到兩者的區別,可以歸納出3 點:
1、for /r 列舉出來的路徑最後都帶有斜槓和點號,而 dir 語句則沒有,會
對獲取到的路徑進行進一步加工產生影響;
2、for /r 不能列舉帶隱藏屬性的目錄,而 dir 語句則可以通過指定 /a 後面
緊跟的參數來獲取帶指定屬性的目錄,更加靈活;
3、若要對獲取到的路徑進行進一步處理,則需要把 dir 語句放入 for /f 語
句中進行分析,寫成 for /f %%i in ('dir /ad /b /s') do „„ 的形式;由於 for /r 語
句是邊列舉路徑邊進行處理,所以,在處理大量路徑的時候,前期不會感到有停
頓,而 for /f 語句則需要等到 dir /ad /b /s 語句把所有路徑都列舉完之後,再讀
入內存進行處理,所以,在處理大量路徑的時候,前期會感到有明顯的停頓。
第2 點差別很容易被大家忽視,導致用 for /r 列舉路徑的時候會造成遺漏;
而第3 點則會讓大家有更直觀的感受,很容易感覺到兩者之間的差別。
要是“元素集合”不是點號呢?那又如何?
來看看這個代碼:
[code22]
@echo off
for /r d:\test %%i in (a b c) do echo %%i
pause
運行的結果是:
[quote]
D:\test\1\a
D:\test\1\b
D:\test\1\c
D:\test\2\a
D:\test\2\b
D:\test\2\c
D:\test\3\a
D:\test\3\b
D:\test\3\c
[/quote]
原來,它的含義是:列舉 d:\test 及其所有的子目錄,對所有的目錄路徑都
分別添加a、b、c 之後再顯示出來。
再來看一個代碼:
[code23]
@echo off
for /r d:\test %%i in (*.txt) do echo %%i
pause
運行結果是:
[quote]
D:\test\test.txt
D:\test\1\1.txt
D:\test\1\2.txt
D:\test\2\a.txt
D:\test\2\b.txt
D:\test\3\1.txt
[/quote]
這段代碼的含義是:列舉 d:\test 及其所有子目錄下的 txt 文本文件(以.txt
結尾的文件夾不會被列出來)。
我們再回過頭來歸納一下這個語句的作用:
[quote]
for /r 目錄 %%i in (元素集合) do 命令語句集合
[/quote]
上面語句的作用是:
1、列舉“目錄”及該目錄路徑下所有子目錄,並把列舉出來的目錄路徑和
元素集合中的每一個元素拼接成形如“目錄路徑\元素”格式的新字符串,然後,
對每一條這樣的新字符串執行“命令語句集合”中的每一條命令;
特別的是:當“元素集合”帶以點號分隔的通配符?或*的時候,把“元素集
合”視爲文件(不視爲文件夾),整條語句的作用是匹配“目錄”所指文件夾及
其所有子文件夾下匹配的文件;若不以點號分隔,則把“元素集合”視爲文件夾
(不視爲文件);
2、當省略掉“目錄”時,則針對當前目錄;
3、當元素集合中僅僅是一個點號的時候,將只列舉目錄路徑;
(二)f or /r 還是 dir /ad /b /s?列舉目錄時該如何
選擇
前面已經說過,當列舉目錄時,for /r 和 dir /ad /b /s 的效果是非常類似的,
這就產生了一個問題:當我要獲取目錄路徑並進行進一步處理的時候,兩者之間,
我該如何選擇?
這個問題,前面其實已經有過一些討論,現在我們再來作詳細的分析。
我們來看一下兩者各自的優缺點:
1、for /r:
1)優點:
① 只通過1 條語句就可以同時實現獲取目錄路徑和處理目錄路徑的操
作;
② 遍歷文件夾的時候,是邊列舉邊處理的,獲取到一條路徑就處理一
條路徑,內存佔用小,處理大量路徑的時候不會產生停頓感;
2)缺點:
① 不能獲取到帶隱藏屬性的目錄,會產生遺漏;
② 不能獲取帶指定屬性的目錄
2、dir /ad /s:
1)優點:
① 能一次性獲取帶任意屬性的目錄,不會產生遺漏;
② 能通過指定不同的參數獲取帶任意屬性的目錄,更具靈活性。
2)缺點:
① dir /ad /s 語句僅能獲取到目錄路徑,若要實現進一步的處理,還需
要嵌入 for /f 語句中才能實現,寫法不夠簡潔;
② 嵌入 for /f 語句之後,需要寫成 for /f "delims=" %%i in ('dir /ad /b /s')
do „„ 的格式,受 for /f 語句運行機制的制約,需要先列舉完所有的路徑放入
內存之後,才能對每一條路徑進行進一步的處理,處理大量路徑時,內存佔用量
偏大,並且在前期會產生明顯的停頓感,用戶體驗度不夠好;
綜合上述分析,可以做出如下選擇:
1、若僅僅是爲了獲取某文件夾及其所有子文件夾的路徑的話,請選擇 dir
/ad /b /s 語句;
2、若需要過濾帶隱藏屬性的文件夾的話,for /r 和 dir 語句都可以實現,
但 for /r 內存佔用小,處理速度快,是上上之選;
3、若需要獲取所有文件夾,則除了 dir /ad /b /s 外,別無選擇,因爲 for /r
語句會遺漏帶隱藏屬性的文件夾;
在實際的使用中,我更喜歡使用 for /f 和 dir 的組合,因爲它不會產生遺
漏,並能給我帶來更靈活的處理方式,唯一需要忍受的,就是它在處理大量路徑
時前期的停頓感,以及在這背後稍微有點偏高的內存佔用;在我追求速度且可以
忽略帶隱藏屬性的目錄的時候,我會換用 for /r 的方案,不過這樣的情形不多—
—有誰會願意爲了追求速度而容忍遺漏呢?
五、僅僅爲了匹配第一層目錄而存在:
for /d
for /d 中 /d ,完整的含義是 /directory,本意是爲了處理文件夾,它的完整
語句應該是這樣的:
[quote]
for /d %%i in (元素集合) do 命令語句集合
[/quote]
當“元素集合”中包含有通配符?或*時,它會匹配文件夾,但是,相比 for /r
而言,這個時候的for /d,其作用就小得可憐了:它僅能匹配當前目錄下的第一
級文件夾,或是指定位置上的文件夾,而不能匹配更深層次的子文件夾。
例如:for /d %%i in (d:\test*) do echo %%i 這樣的語句 ,會匹配到形如:
d:\test、d:\test1、d:\test2 之類的文件夾,若不存在這樣的路徑,將不會有任何回
顯。
當“元素集合”中不包含任何的通配符時,它的作用和 "for %%i in (元素集
合) do 命令語句集合" 這樣的語句別無二致。
因此,for /d 的角色就變得很微妙了:當“元素集合”中包含通配符?或*時,
它的作用就是匹配文件夾,此時,它僅能匹配當前目錄下的第一級文件夾,或是
指定位置上的文件夾,在層次深度上不及 for /r,但和 for /r 一樣的壞脾氣:不
能匹配帶隱藏屬性的文件夾;在靈活性上不及for /f 和dir 的組合;當“元素集
合”中不包含任何統配符的時候,它完全是 "for %%i in (元素集合) do „„" 語
句的翻版,但是又稍顯複雜。
for /d 的作用是如此有限,我使用的次數是如此之少,以至於我一度找不到
它的用武之地,認爲它食之無味,棄之可惜,完全是雞肋一塊。
某年某月,我在cmd 窗口裏寫下了這樣的代碼:
[code24]
for /d %i in (test*) do @echo %i
我的本意是想查看在我的臨時目錄下,長年累月的測試工作到底建立了多少
測試文件夾,以便我隨後把echo 換成rd 刪除之。這個時候,我發現這條代碼是
如此的簡潔,是 for /r 或 for 和 dir /ad /b 的組合所無法替代的(echo 換成rd
就可以直接刪除掉這些測試目錄)。 簡潔的代碼給我帶來的喜悅僅僅持續了短短10 幾秒的時間,我便開始了迷
惘——能用到for /d 的類似情形,貌似少之又少且乏善可陳啊。
六、計數循環:for /l /l 者,/loop 的縮寫是也,從鳥語翻譯過來,loop 就是循環的意思。實際上,
所有的for 語句,都可以看成是一種“循環”,只是在/l 中,特指按照指定次數進
行循環罷了。 for /l 語句的完整格式是這樣的:for /l %%i in (x,y,z) do („„),在這個語句
中,x、y 和z 都只能取整數,正負皆可,x 指代起始值,y 指代步長,z 爲終止
值,具體含義爲:從x 開始計數,以y 爲步長,直至最接近z 的那個整數值爲止,
這之間有多少個數,do 後的語句就執行多少次。 舉個具體的例子: [code25]
for /l %%i in (1,2,10) do echo bathome 在以上的代碼中,初始值是1,步長爲2,終止值爲10,表明計數從1 開始,
每隔2 個數計算一次,直至最接近10 的那個整數,羅列出來,就是1,3,5,7,9,
再下一個就是11,超過10 了,不再計算在內,所以,do 後的語句只執行5 次,
將連續顯示5 個bathome。 實際上,x,y 和z 的值可正可負,甚至爲0,限制非常寬鬆:
1、步長y 的值不能爲0;
2、當步長y 的值爲正整數時,終止值z 不能小於初始值x;
3、當步長y 的值爲負整數的時候,終止值z 不能大於初始值x。 換而言之,必須保證in 和do 之間能取到一個有效的數組序列。 例如: [code26]
for /l %%i in (-1,2,5) do echo bathome [code27]
for /l %%i in (5,-2,-1) do echo bathome 以上兩條代碼的功能完全一樣,都將顯示4 次bathome,區別就在於[code26]
是正序計算,而[code27]是逆序計數而已。
以下幾條代碼都是有問題的:
[code28]
for /l %%i in (1,0,1) do echo bathome
[code29]
for /l %%i in (2,1,1) do echo bathome
[code30]
for /l %%i in (1,-1,2) do echo bathome
其中,[code28]違反了步長不能爲 0 的限制,將陷入無限循環中;[code29]
和[code30]都犯了同樣的錯誤:無法獲得有效的數列元素,導致 in 和do 之間取
到的值爲空元素,從而使得整條for 語句無從執行。 當大家明白了 for /l 的具體功能之後,是否會想到了與它有異曲同工之妙的
goto 循環語句呢?似乎,for /l 和 goto 循環語句可以相互替換? 一般而言,for /l 語句可以換成goto 循環,但是,goto 循環並不一定能被 for
/l 語句替換掉。具體原因,請大家仔細想想,我在此不再詳細解說,只是就大家
非常關心的一個問題提供一個簡潔的答案,那就是:什麼時候該用 for /l 計數循
環,而什麼時候又該用goto 條件循環? 答案非常簡單:當循環次數確定的時候,首選 for /l 語句,也可使用 goto
語句但不推薦;當循環次數不確定的時候,用goto 語句將是唯一的選擇,因爲,
這個時候需要用if 之類的條件語句來判斷何時結束goto 跳轉。
後記:
當Windows 爲我們打開了五彩繽紛的圖形窗口的時候
DOS 命中註定會隕落
CMD 毫無懸念將萎縮
批處理逐漸趨向無聲無息
而powershell 的到來,無疑會讓更多的人忘記批處理
這是一門即將失傳的技藝
這是一塊行將就木的領域
然而,命令行工具仍然具有批量處理一切的巨大威力
字符界面仍然是高效操作的代名詞
曾爲批處理的方便靈活而擊節讚賞
曾被批處理的簡潔快速深深折服
一直以來,總想爲批處理的推廣做些什麼
於是,從在 CN-DOS 裏盡職盡責地爲大家解答疑問,到創辦了自己的論壇
專職答疑解惑,再到無怨無悔地碼字寫教程,一步步走來,喜怒哀愁,五味雜陳
直至如今辭去站長等一切管理職務,逐漸淡出批處理圈子
夢依舊在,只是,心有餘而力漸有不足
這篇從入門到精通的教學帖,從2008 年10 月開貼到現在,不知不覺拖拖拉
拉竟然過去了兩年有餘
每每看到跟帖的會員在問什麼時候有更新
心中總有一絲愧疚
今天,終於抽空對它做個了斷
只是,年年歲歲花相似,歲歲年年人不同
繁雜的事務使我已不再有當初的心境
for /l 部分總有虎頭蛇尾的感覺
只能向各位說聲抱歉了
在我徹底淡出批處理圈子之前
我只能盡我所能地向各位傾我所學了
最後,我希望論壇的管理人員能按照頂樓的管理提示經常爲這個帖子抽抽水
或者是爲了大家閱覽的方便而永久鎖定這個帖子