批處理函數概述
批處理函數一段可以被批處理文件通過(guò)可選參數進(jìn)行調用的代碼。函數執行指定的任務(wù),然后返回到主調程序,它可以避免無(wú)意中修改其它代碼中的變量。
函數的最主要好處是代碼的可重用性。一旦函數被寫(xiě)好之后,它就能被重復地使用。這樣可以減少開(kāi)發(fā)和調試代碼的時(shí)間。
使用函數的代碼自然具有模塊化的結構,而且很少使用全局變量,所以他們更加容易讀懂、調試和維護。
函數庫中的所有函數都基于如下模板:
| <函數名稱(chēng)> | 函數的名稱(chēng),比如 GetDate |
| <變量列表> | 變量列表僅僅用來(lái)提示該如何調用該函數(函數名之后的內容全部被命令行解釋器忽略) |
| <作者/日期/版本信息> | 不用說(shuō)大家都明白 |
| <函數描述> | 簡(jiǎn)要說(shuō)明函數的功能是什么以及使用于哪些平臺(NT4、W2K 或者 XP) |
| <變量描述> | 函數變量的詳細描述 |
| <函數主體> | 這里用來(lái)實(shí)現函數主要功能 |
| <返回值> | 這里用來(lái)把函數的局部變量返回給主調程序 |
要使用什么函數,只需要把它們復制并粘貼到你的批處理文件尾部。為了避免函數在你的主批處理結束后被再次執行,請在主批處理和函數之間添加 goto :EOF 語(yǔ)句。比如:
實(shí)例 1a
[本文檔中的每個(gè)函數都包含一個(gè)實(shí)例,用來(lái)演示函數應該如何被調用。在這些實(shí)例中,函數本身被省略了,以避免不必要的重復代碼。當使用任何一個(gè)實(shí)例時(shí),記得把函數追加到你的批處理文件尾部。]
本文檔中的每個(gè)函數都描述了必選和可選參數個(gè)數、參數類(lèi)型以及參數是否應該使用傳值調用或者引用調用。這兩個(gè)術(shù)語(yǔ)在此一帶而過(guò),預知詳情如何,且看下回分解。不嚴格地說(shuō),傳值調用就是使用常量或者擴展變量去調用函數,引用調用就是使用變量名去調用函數。
要使用傳值調用的方法傳遞一個(gè)值,可以用包含該值的常量或者擴展變量去調用函數。比如,Sleep 函數需要一個(gè)傳值調用的參數(秒數)。下面是使用數值9來(lái)調用 Sleep 函數的三個(gè)方法:
實(shí)例Example 2a
實(shí)例Example 2b
實(shí)例Example 2c
注:在實(shí)例 2b 中,幾乎任意變量名都可以被使用。實(shí)例 2c 中假設數值9已經(jīng)通過(guò)命令行參數的方式傳遞給了批處理文件。
要使用引用調用的方法傳遞一個(gè)值,使用變量名即可。比如, GetDate 函數需要三個(gè)引用調用的參數, 下面來(lái)演示如何調用 GetDate 函數并使用其返回值:
注:雖然實(shí)例 3a 中使用的變量名是 yy、mm 和 dd,但實(shí)際上幾乎任意變量名都可以被使用。 實(shí)例 3b 在功能上和實(shí)例 3a 是相同的(但可讀性較差)。
大部分高級編程語(yǔ)言都允許使用傳值調用和引用調用的方式來(lái)傳遞參數。傳值調用意味著(zhù)函數接受到的是一個(gè)變量值的副本。函數對該副本的任何更改都不會(huì )影響原始變量。
當一個(gè)變量被使用引用調用的方式來(lái)傳遞時(shí),函數接受到的是一個(gè)指向原始變量的指針,不嚴格地說(shuō),函數對該指針的修改會(huì )影響原始變量。
在批處理編程語(yǔ)言中,變量以引用調用的方式來(lái)傳遞。 不幸的是,指向原始變量的指針是只讀的,無(wú)法被賦值,因此也就無(wú)法更改其指向的變量。
幸好,只需略施小計,批處理語(yǔ)言就能像 C 和 Visual Basic 等高級語(yǔ)言那樣使用傳值調用和引用調用的方式來(lái)傳遞參數。
下面是一個(gè)簡(jiǎn)單的例子,來(lái)說(shuō)明如何通過(guò)傳值調用來(lái)傳遞參數,以及函數如何讀取參數的值。
在第三行中,%str% 被擴展為 Hello,然后 Hello 在內存中實(shí)際存儲的位置被賦值給 %1。在第七行中引用已經(jīng)被擴展為 Hello 的 %1。
如果兩個(gè)或多個(gè)參數被傳遞了過(guò)來(lái),可以用 %2、%3 等等一直到 %9 來(lái)引用它們。如果傳遞過(guò)來(lái)的變量超過(guò)九個(gè),要想引用第十個(gè)參數,就必須使用 shift 命令。
shift 產(chǎn)生的效果會(huì )影響所有的指針。在使用 shift 之前,%0 指向函數或者主調程序本身的名字,%1 指向第一個(gè)參數。使用 shift 之后,%0 指向 %1 剛剛所指向的地方,%1 指向 %2 剛剛所指向的地方,以此類(lèi)推?,F在就可以用 %9 來(lái)引用第十個(gè)參數了。
下面的例子和上例略有不同。傳遞給函數的是包含 Hello 這個(gè)值的變量名,而不是 Hello 這個(gè)值本身。
在函數 demoB 中,%1 擴展為 str。要想讀取包含在 str 中的值,需要使用兩個(gè)百分號來(lái)閉合 %1 并告訴命令行解釋器進(jìn)行再一次擴展。這一點(diǎn)可以用第七行中的 CALL 來(lái)實(shí)現。關(guān)于變量擴展的詳細解釋?zhuān)蓞⒖?alt.msdos.batch.nt 中的 "Variable expansion (W2KSP2)"。
為什么我們想要以這種方式傳遞參數呢?這樣可以讓函數獲得變量的名稱(chēng)、給變量賦值、返回數值。
目前為止的例子只是為了闡述前面給出的一些觀(guān)點(diǎn)。下面一個(gè)例子將會(huì )展示函數是如何返回一個(gè)值的。這也是本函數庫中的一個(gè)典型例子。這個(gè)方法可以保證函數可以被插入到任何批處理文件,且不產(chǎn)生任何副作用。
這是一個(gè)面積函數,它通過(guò)物體的寬和高來(lái)簡(jiǎn)單地計算出面積,并把該值返回。共需要三個(gè)參數,前面兩個(gè)使用傳值調用,第三個(gè)使用引用調用。
[本函數庫約定,函數名稱(chēng)后面應該緊跟著(zhù)寫(xiě)清楚函數的參數列表(見(jiàn)下例第八行)。參數列表放在這個(gè)地方非常合適,因為命令行解釋器會(huì )忽略函數名之后的所有內容。參數列表的僅僅是為了提示該函數需要的參數類(lèi)型和個(gè)數,沒(méi)有其它目的。]
第四行調用面積函數,并傳入三個(gè)參數(為方便閱讀可以看做 call :Area 2 3 answer)。函數的第一個(gè)命令(第九行)是 setlocal。該命令為當前環(huán)境創(chuàng )建一個(gè)副本,并把該副本變成當前環(huán)境。第十行計算物體的參數 %1 和 %2 并把結果保存到變量 res 中。
下面一行包含由 & 分割的開(kāi)來(lái)的兩個(gè)命令。命令行解釋器對兩個(gè)命令進(jìn)行同時(shí)擴展,然后依次執行。所以在第十一行,擴展之后就等同于 endlocal & set "answer=6"。命令 endlocal 被執行時(shí),會(huì )刪除由最近一個(gè) setlocal 命令創(chuàng )建的環(huán)境副本,并回退到前面一個(gè)環(huán)境。然后 set "answer=6" 被執行,最后在第十二行用 goto :EOF 推出函數并繼續執行第五行。最后結果就是面積函數返回了一個(gè)值。
在第十一行中,如果命令 set "%3=%res%" 是在 endlocal 執行之后被擴展的,結果將會(huì )變成 set "answer=",而非擴展為 set "answer=6",因為變量 res 那時(shí)已經(jīng)被 endlocal 命令刪除了。
經(jīng)驗的做法是使用傳值調用來(lái)傳遞參數,除非你需要使用引用調用的方式傳遞參數來(lái)改變原始變量的值。上面的面積函數正式演示了這個(gè)經(jīng)驗法則,第一個(gè)和第二個(gè)參數(寬和高)使用傳值調用,為了能夠把計算結果賦值給變量 answer,該參數使用引用調用的方式來(lái)進(jìn)行傳遞。
盡管引用調用是只讀訪(fǎng)問(wèn)的,有時(shí)仍然需要用這種方式來(lái)傳遞參數。比如,當傳遞兩個(gè)或者多個(gè)未閉合的參數時(shí),如果某個(gè)參數中包含空格,那么使用傳值調用的話(huà),函數將無(wú)法確定參數之間的關(guān)系。引用調用也可以用來(lái)傳遞那些包含特殊字符(比如 " ! | < > ^ " & %)的參數。
為了創(chuàng )建正真可重用的函數,你必須能夠保證它們不會(huì )無(wú)意中修改自己范圍之外的其它代碼中的變量。要實(shí)現這一點(diǎn),可以使用 setlocal 和 endlocal 聲明來(lái)閉合函數主體,就像上例中展示的那樣。
不過(guò),假如用引用調用傳遞兩個(gè)或者多個(gè)參數并且這些參數需要被你的函數讀?。ㄅc僅需賦值截然不同),這時(shí)就會(huì )變得有些復雜。下文中交換函數正好最生動(dòng)地說(shuō)明了這一點(diǎn)。
用引用調用傳遞兩個(gè)或者多個(gè)參數且這些參數需要被讀取時(shí),千萬(wàn)要小心,在參數全部被讀取之前不要進(jìn)行任何毀壞。假設有下例:
上面的代碼執行結果為:
這個(gè)函數的本意是要交換兩個(gè)變量的內容。注意,第一次成功了,但是第二次失敗了。這是因為函數外部的變量和函數內部的同名變量發(fā)生了沖突。第二次交換的具體出錯情況如下:
解決此問(wèn)題的常見(jiàn)方法是,在函數中使用唯一的變量名,通常是函數名加一個(gè)句點(diǎn)再加變量名。其實(shí),如果要使用這種變量命名方式,只需為敏感變量使用唯一變量名即可。重新編寫(xiě) Swap 函數中的第十四至第十六行,以避免變量在讀取之前就被覆蓋,如下所示:
14. call set Swap.a=%%%1%%
15. call set b=%%%2%%
16. endlocal&set %1=%b%&set %2=%Swap.a%&goto :EOF
這個(gè)方法有幾個(gè)缺點(diǎn)。第一,你不能在函數之外的變量名中使用句點(diǎn)。第二,如果函數名稱(chēng)較長(cháng),而且需要讀取多個(gè)引用調用變量,函數中的代碼行數可能會(huì )成倍增加(如果你想避免“長(cháng)”代碼行)。
這里有個(gè)備選方案,使用 FOR /F 命令來(lái)讀取變量,用任何參數值中都不存在的字符作為列分隔符??梢杂眠@個(gè)方法把 Swap 函數改寫(xiě)如下:
這里的分割字符是一個(gè)“?”(對應的 ASCII 碼是172,形狀就像是倒著(zhù)寫(xiě)的字母“L”)。之所以選擇這個(gè)字符,是因為其它地方幾乎不會(huì )用到它。在美式鍵盤(pán)的 TAB 鍵上方可以找到它。如果沒(méi)有該按鍵,你可以按住 Alt 鍵不放的同時(shí)用數字小鍵盤(pán)輸入172來(lái)得到這個(gè)字符。這個(gè)方法的唯一缺點(diǎn)就是,你的變量中不能包含分割字符,否則你可能會(huì )得到錯誤的結果。
這個(gè)問(wèn)題在 alt.msdos.batch.nt 中的 "Function parameters" 里面也有提到。
聯(lián)系客服