How to use "for/" batch command in Dos extention

iihero發表於2012-03-07
以前常覺得DOS的 命令列功能太弱,無法象UNIX一樣可以用命令列完成非常複雜的操作。實際上,當MS從WIN2K開始將命令列增強後,已經借鑑了相當多UNIX的優點, 雖然還無法做到象UNIX那麼靈活,但已可完成絕大多數的任務,比如用&&和||連線兩個(或更多)命令,由前一個的返回值來決定下一個 是否執行,等等。而在這些增強中,最明顯的,就是FOR命令。

舉個例子,用適當的引數,可用FOR命令將 date /t 的輸出 從 "Sat 07/13/2002" 變成你想要的格式,比如, "2002-07-13":

c:>for /f "tokens=2,3,4 delims=/ " %a in ('date /t') do @echo %c-%a-%b 2002-07-13

該例將在(3)中詳細說明。

0. 基本應用

簡單說,FOR是個迴圈,可以用你指定的迴圈範圍生成一系列命令。最簡單的例子,就是人工指定迴圈範圍,然後對每個值執行指定的命令。例如,想快速報告每個硬碟分割槽的剩餘空間:

for %a in (c: d: e: f:) do @dir %a|find "可用位元組"

將輸出:

8 Dir(s) 1,361,334,272 bytes free
15 Dir(s) 8,505,581,568 bytes free
12 Dir(s) 12,975,149,056 bytes free
7 Dir(s) 11,658,854,400 bytes free

用它可以使一些不支援萬用字元的命令對一系列檔案進行操作。在WIN9X中,TYPE命令(顯示檔案內容)是不支援*.txt這種格式的(WIN2K開始TYPE已支援通配)。遇到類似情況就可以用FOR:

for %a in (*.txt) do type %a

這些還不是FOR最強大的功能。我認為它最強大的功能,表現在以下這些高階應用:

1. 可以用 /r 引數遍歷整個目錄樹
2. 可以用 /f 引數將文字檔案內容作為迴圈範圍
3. 可以用 /f 引數將某一命令執行結果作為迴圈範圍
4. 可以用 %~ 操作符將檔名分離成檔名、副檔名、碟符等獨立部分


現分別舉例說明如下:

1. 用 /r 遍歷目錄樹

當用 *.* 或 *.txt 等檔名萬用字元作為 for /r 的迴圈範圍時,可以對當前目錄下所有檔案(包括子目錄裡面的檔案)進行操作。舉個例子,你想在當前目錄的所有txt檔案(包括子目錄)內容中查詢"bluebear"字樣,但由於find本身不能遍歷子目錄,所以我們用for:

for /r . %a in (*.txt) do @find "bluebear" %a

find 前面的 @ 只是讓輸出結果不包括 find 命令本身。這是DOS很早就有的功能。和FOR無關。

當用 . 作為迴圈範圍時,for 只將子目錄的結構(目錄名)作為迴圈範圍,而不包括裡面的檔案。有點象 TREE 命令,不過側重點不同。TREE 的重點是用很漂亮易讀的格式輸出,而FOR的輸出適合一些自動任務,例如,我們都知道用CVS管理的專案中,每個子目錄下都會有一個CVS目錄,有時在軟體發行時我們想把這些CVS目錄全部去掉:

for /r . %a in (.) do @if exist %aCVS rd /s /q %aCVS

先用 if exist 判斷一下,是因為 for 只是機械的對每個目錄進行列舉,如果有些目錄下面沒有CVS也會被執行到。用 if exist 判斷一下比較安全。

這種刪除命令威力太大,請小心使用。最好是在真正執行以上的刪除命令前,將 rd /s /q 換成 @echo 先列出要刪出的目錄,確認無誤後再換回rd /s /q:

for /r . %a in (.) do @if exist %aCVS @echo %aCVS

可能目錄中會多出一層 ".",比如 c:projrelease.CVS ,但不會影響命令的執行效果。

2. 將某一檔案內容或命令執行結果作為迴圈範圍:

假如你有一個檔案 todel.txt,裡面是所有要刪除的檔案列表,現在你想將裡面列出的每個檔案都刪掉。假設這個檔案是每個檔名佔一行,象這樣:

c:tempa1.txt c:tempa2.txt c:tempsubdirb3.txt c:tempsubdirb4.txt

那麼可以用FOR來完成:

for /f %a in (todel.txt) do del %a

這個命令還可以更強大。比如你的 todel.txt 並不是象上面例子那麼幹淨,而是由DIR直接生成,有一些沒用的資訊,比如這樣:

Volume in drive D is DATA
Volume Serial Number is C47C-9908

Directory of D:tmp

09/26/2001 12:50 PM 18,426 alg0925.txt
12/02/2001 04:29 AM 795 bsample.txt
04/11/2002 04:18 AM 2,043 invitation.txt
4 File(s) 25,651 bytes
0 Dir(s) 4,060,700,672 bytes free

for 仍然可以解出其中的檔名並進行操作:

for /f "skip=5 tokens=5" %a in (todel.txt) do @if exist %a DEL %a

當然,上面這個命令是在進行刪除,如果你只是想看看哪些檔案將被操作,把DEL換成echo:

for /f "skip=5 tokens=5" %a in (todel.txt) do @if exist %a echo %a

你將看到:

alg0925.txt
bsample.txt
invitation.txt

skip =5表示跳過前5行(就是DIR輸出的頭部資訊),tokens=5表示將每行的第5列作為迴圈值放入%a,正好是檔名。在這裡我加了一個檔案存在判 斷,是因為最後一行的"free"剛好也是第5列,目前還想不出好的辦法來濾掉最後兩行,所以檢查一下可保萬無一失。

3. 可以用 /f 引數將某一命令執行結果作為迴圈範圍

非常有用的功能。比如,我們想知道目前的環境變數有哪些名字(我們只要名字,不要值)。可是SET命令的輸出是"名字=值"的格式,現在可以用FOR來只取得名字部分:

FOR /F "delims==" %i IN ('set') DO @echo %i

將看到:

ALLUSERSPROFILE
APPDATA
CLASSPATH
CommonProgramFiles
COMPUTERNAME
ComSpec
dircmd
HOMEDRIVE ......

這裡是將set命令執行的結果拿來作為迴圈範圍。delims==表示用=作為分隔符,由於FOR /F預設是用每行第一個TOKEN,所以可以分離出變數名。如果是想僅列出值:

FOR /F "delims== tokens=2" %i IN ('set') DO @echo %i

tokens=2和前例相同,表示將第二列(由=作為分隔符)作為迴圈值。

再來個更有用的例子:

我們知道 date /t (/t表示不要詢問使用者輸入)的輸出是象這樣的:

Sat 07/13/2002

現在我想分離出日期部分,也就是13:

for /f "tokens=3 delims=/ " %a in ('date /t') do @echo %a

實際上把 tokens後面換成1,2,3或4,你將分別得到Sat, 07, 13和2002。注意delims=/後面還有個空格,表示/和空格都是分隔符。由於這個空格delims必須是/f選項的最後一項。

再靈活一點,象本文開頭提到的,將日期用2002-07-13的格式輸出:

for /f "tokens=2,3,4 delims=/ " %a in ('date /t') do @echo %c-%a-%b

當tokens後跟多個值時,將分別對映到%a, %b, %c等。實際上跟你指定的變數有關,如果你指定的是 %i, 它們就會用%i, %j, %k等。

靈活應用這一點,幾乎沒有做不了的事。

4. 可以用 %~ 操作符將檔名分離成檔名、副檔名、碟符等獨立部分

這個比較簡單,就是說將迴圈變數的值自動分離成只要檔名,只要副檔名,或只要碟符等等。

例:要將 c:mp3下所有mp3的歌名列出,如果用一般的 dir /b/s 或 for /r ,將會是這樣:

g:mp3Archived5-18-01-A游鴻明-下沙游鴻明-01 下沙.mp3
g:mp3Archived5-18-01-A游鴻明-下沙游鴻明-02 21個人.mp3
......
g:mp3Archived5-18-01-A王菲-寓言王菲-阿修羅.mp3
g:mp3Archived5-18-01-A王菲-寓言王菲-彼岸花.mp3
g:mp3Archived5-18-01-A王菲-寓言王菲-不愛我的我不愛.mp3
......

如果我只要歌名(不要路徑和".mp3"):

游鴻明-01 下沙
游鴻明-02 21個人
......
王菲-阿修羅
王菲-彼岸花
王菲-不愛我的我不愛
......

那麼可以用FOR命令:

for /r g:mp3 %a in (*.mp3) do @echo %~na

凡是 %~ 開頭的操作符,都是檔名的分離操作。具體請看 for /? 幫助。

本文舉的例子有些可能沒有實際用處,或可用其它辦法完成。僅用於體現FOR可以不借助其它工具,僅用DOS命令組合,就可完成相當靈活的任務。

具體請看 for /? 幫助

For命令具體解釋

FOR %variable IN (set) DO command [command-parameters]

%variable 指定一個單一字母可替換的引數。
(set) 指定一個或一組檔案。可以使用萬用字元。
command 指定對每個檔案執行的命令。
command-parameters
為特定命令指定引數或命令列開關。

在批處理檔案中使用 FOR 命令時,指定變數請使用 %%variable 而不要用 %variable。變數名稱是區分大小寫的,所以 %i 不同於 %I.

如果命令副檔名被啟用,下列額外的 FOR 命令格式會受到支援:

FOR /D %variable IN (set) DO command [command-parameters]

如果集中包含萬用字元,則指定與目錄名匹配,而不與檔名匹配。

FOR /R [[drive:]path] %variable IN (set) DO command [command-parameters]

檢查以 [drive:]path 為根的目錄樹,指向每個目錄中的FOR 語句。如果在 /R 後沒有指定目錄,則使用當前目錄。如果集僅為一個單點(.)字元,則列舉該目錄樹。

FOR /L %variable IN (start,step,end) DO command [command-parameters]

該集表示以增量形式從開始到結束的一個數字序列。因此,(1,1,5) 將產生序列 1 2 3 4 5,(5,-1,1) 將產生序列 (5 4 3 2 1)。

FOR /F ["options"] %variable IN (file-set) DO command [command-parameters]
FOR /F ["options"] %variable IN ("string") DO command [command-parameters]
FOR /F ["options"] %variable IN ('command') DO command [command-parameters]

或者,如果有 usebackq 選項:

FOR /F ["options"] %variable IN (file-set) DO command [command-parameters]
FOR /F ["options"] %variable IN ("string") DO command [command-parameters]
FOR /F ["options"] %variable IN ('command') DO command [command-parameters]

filenameset 為一個或多個檔名。繼續到 filenameset 中的下一個檔案之前,每份檔案都已被開啟、讀取並經過處理。處理包括讀取檔案,將其分成一行行的文字,然後將每行解析成零或更多的符號。然後用已找到的符號字串變數值呼叫 For 迴圈。以預設方式,/F 通過每個檔案的每一行中分開的第一個空白符號。跳過空白行。您可通過指定可選 "options" 引數替代預設解析操作。這個帶引號的字串包括一個或多個指定不同解析選項的關鍵字。這些關鍵字為:

eol=c - 指一個行註釋字元的結尾(就一個)
skip=n - 指在檔案開始時忽略的行數。
delims=xxx - 指分隔符集。這個替換了空格和跳格鍵的
預設分隔符集。
tokens=x,y,m-n - 指每行的哪一個符號被傳遞到每個迭代
的 for 本身。這會導致額外變數名稱的分配。m-n
格式為一個範圍。通過 nth 符號指定 mth。如果
符號字串中的最後一個字元星號,
那麼額外的變數將在最後一個符號解析之後
分配並接受行的保留文字。
usebackq - 指定新語法已在下類情況中使用:
在作為命令執行一個後引號的字串並且一個單
引號字元為文字字串命令並允許在 filenameset
中使用雙引號擴起檔名稱。

某些範例可能有助:

FOR /F "eol=; tokens=2,3* delims=, " %i in (myfile.txt) do @echo %i %j %k

會分析 myfile.txt 中的每一行,忽略以分號打頭的那些行,將每行中的第二個和第三個符號傳遞給 for 程式體;用逗號和/或空格定界符號。請注意,這個 for 程式體的語句引用 %i 來取得第二個符號,引用 %j 來取得第三個符號,引用 %k 來取得第三個符號後的所有剩餘符號。對於帶有空格的檔名,您需要用雙引號將檔名括起來。為了用這種方式來使用雙引號,您還需要使用 usebackq 選項,否則,雙引號會被理解成是用作定義某個要分析的字串的。

%i 專門在 for 語句中得到說明,%j 和 %k 是通過 tokens= 選項專門得到說明的。您可以通過 tokens= 一行指定最多 26 個符號,只要不試圖說明一個高於字母 'z' 或 'Z' 的變數。請記住,FOR 變數是單一字母、分大小寫和全域性的;而且,同時不能有 52 個以上都在使用中。

您還可以在相鄰字串上使用 FOR /F 分析邏輯;方法是,用單引號將括號之間的 filenameset 括起來。這樣,該字串會被當作一個檔案中的一個單一輸入行。

最後,您可以用 FOR /F 命令來分析命令的輸出。方法是,將括號之間的 filenameset 變成一個反括字串。該字串會被當作命令列,傳遞到一個子 CMD.EXE,其輸出會被抓進記憶體,並被當作檔案分析。因此,以下例子:

FOR /F "usebackq delims==" %i IN (`set`) DO @echo %i

會列舉當前環境中的環境變數名稱。

另外,FOR 變數參照的替換已被增強。您現在可以使用下列選項語法:

~I - 刪除任何引號("),擴充 %I
%~fI - 將 %I 擴充到一個完全合格的路徑名
%~dI - 僅將 %I 擴充到一個驅動器號
%~pI - 僅將 %I 擴充到一個路徑
%~nI - 僅將 %I 擴充到一個檔名
%~xI - 僅將 %I 擴充到一個副檔名
%~sI - 擴充的路徑只含有短名
%~aI - 將 %I 擴充到檔案的檔案屬性
%~tI - 將 %I 擴充到檔案的日期/時間
%~zI - 將 %I 擴充到檔案的大小
%~$PATH:I - 查詢列在路徑環境變數的目錄,並將 %I 擴充
到找到的第一個完全合格的名稱。如果環境變數名
未被定義,或者沒有找到檔案,此組合鍵會擴充到
空字串

可以組合修飾符來得到多重結果:

%~dpI - 僅將 %I 擴充到一個驅動器號和路徑
%~nxI - 僅將 %I 擴充到一個檔名和副檔名
%~fsI - 僅將 %I 擴充到一個帶有短名的完整路徑名
%~dp$PATH:i - 查詢列在路徑環境變數的目錄,並將 %I 擴充
到找到的第一個驅動器號和路徑。
%~ftzaI - 將 %I 擴充到類似輸出線路的 DIR

在以上例子中,%I 和 PATH 可用其他有效數值代替。%~ 語法用一個有效的 FOR 變數名終止。選取類似 %I 的大寫變數名比較易讀,而且避免與不分大小寫的組合鍵混合。

相關文章