公司在Windows環(huán)境下進(jìn)行開發(fā),所以在寫自動構(gòu)建的時候,自然而然地想到了CMD SHELL。本來考慮過使用Windows Script Host腳本(WSF、JS或VBS)來寫,但要在WSH腳本里調(diào)用VS的批處理來設(shè)置環(huán)境很困難。隨著項目結(jié)構(gòu)變得復(fù)雜,CMD SHELL寫的構(gòu)建腳本也開始變得復(fù)雜,這個時候就感到CMD SHELL有點吃力了,于是想到了Powershell。
在這之前對Powershell其實有過一些了解,知道它是一個比CMD SHELL更適合寫腳本程序的東西,跟.NET有著莫大的關(guān)系。不過之前的了解也僅此而已,所以還是需要先學(xué)習(xí)一下。通過Google找到了Tobias Weltner博士寫的《Master Powershell》(http://powershell.com/cs/blogs/ebook/),花了半天揀想了解的部分通看了一遍,于是有了寫下《Powershell學(xué)習(xí)筆記》的想法。
感謝Tobias Weltner博士,以及他那本免費(fèi)的《Master Powershell》!
看了《Master Powershell》一遍之后,我覺得學(xué)習(xí)Powershell是正確的。
CMD SHELL(以下簡稱CMD)來源于DOS時代的批處理腳本,它最初的設(shè)計就是為著順序批處理來的。在Windows 2000和Windows XP時代,DOS批處理正式升級為CMD SHELL,在語法和功能上做了一些擴(kuò)展;腳本文件開始支持.CMD擴(kuò)展名,并兼容之前的.BAT擴(kuò)展名;其名稱也由“DOS窗口”改為“命令提示符”。CMD SHELL雖然可以完成很多比較復(fù)雜的任務(wù),但卻非??简?zāi)_本開發(fā)者大腦的堆棧大小。
CMD的確適合寫一些簡單的SHELL程序,而Powershell卻包含了寫一個復(fù)雜的腳本程序所需要的各種支持,包括語法和函數(shù)庫——巨大的.NET函數(shù)庫。
CMD中處理參數(shù),通常是按順序處理%1-%9,參數(shù)比較多還需要通過SHIFT命令來對參數(shù)進(jìn)行移位。如果想處理位置不定的switch參數(shù),或者處理命名參數(shù),那就分析參數(shù)這部分腳本都能把人搞攪暈。而Powershell原生就支持參數(shù)數(shù)組、命名參數(shù)和switch參數(shù)。比如
- # test.ps1
- # 在腳本中定義命名參數(shù)和switch參數(shù)
- param($name, [switch] $isMale)
- # 如果 .\test.ps1 -name "James Fancy" -ismale
- # $name的值為James Fancy
- # $isMale的值為True
- # 如果缺省-ismale參數(shù),$isMale的值為False
- # 如果 .\test.ps1 -name -ismale
- # 會直接報告錯誤,因為-name需要附加的參數(shù)
可見Powershell已經(jīng)將參數(shù)處理部分進(jìn)行了很好的封閉。如果要用CMD來寫,那就需要GOTO、SHIFT若干條件分支以及若干臨時的環(huán)境變量。
CMD中的變量,其實都是環(huán)境變量,而且其值一定是字符串。而Powershell中有真正意義的變量,并且這些變量可以是字符串類型、數(shù)值類型、日期類型……甚至任意對象類型,只要是.NET庫中支持的對象。不錯,Powershell是一個面向?qū)ο蟮哪_本。很酷,是么?
CMD中如果需要列表怎么辦——用分號分隔的字符串值;那如果需要哈希表呢——用分號分隔的帶等號的字符串值……是的,CMD可以做到,只是處理起來麻煩一點而已。當(dāng)然,在Powershell中不需要這么麻煩,Powershell支持?jǐn)?shù)組類型的變量和哈希表類型的變量,就像——嗯,像什么呢?有點像PHP,也有點像Javascript。
- # 定義一個數(shù)組
- $a = 1,2,3
- # 也可以這樣定義
- $a = @(1,2,3)
- # 或者定義一個空數(shù)組
- $a = @()
- # 再定義兩個哈希表
- $m1 = ${ key1="value1";key2=1234 }
- $emptymap = ${}
在CMD中如果想計算四則運(yùn)算,需要用到SET /A命令,可以進(jìn)行常見的各種算術(shù)運(yùn)算。Powershell當(dāng)然不輸于CMD。Powershell中可以進(jìn)行各種各樣的運(yùn)算,而且完全不需要通過命令來進(jìn)行。
當(dāng)然Powershell能做的不僅是這樣。比如獲取日期,CMD下需要獲取日期當(dāng)然是用DATE命令,如果要干凈一點的日期,用DATE /T,不過輸出的日期格式嘛……當(dāng)然就看在Windows里怎么設(shè)置的啦。而在Powershell里,日期是一個對象,格式嘛,當(dāng)然可以想什么樣就什么樣……
- # 按兩種格式輸出日期
- (get-date).toString("yyyy-MM-dd HH:mm:ss")
- # 輸出 2011-10-04 09:54:47
- (get-date).toString("yyyy年M月d日")
- # 輸出 2011年10月4日
對了,還有條件表達(dá)式,Powershell是通過-eq、-ne、-lt、-gt等運(yùn)算符來進(jìn)行比較,還可以通過-and、-or等運(yùn)算符來表達(dá)組合條件……差點忘了-not,當(dāng)然它還可以簡寫成“!”。
- # 下面表達(dá)式返回True
- (1 -lt 2) -and (3 -eq 03)
CMD中想要處理字符串,那簡直就是惡夢!雖然SET和FOR命令外加GOTO或者CALL命令可以對字符串進(jìn)行一些簡單的處理,但是處理起來那是真的太太太復(fù)雜了?,F(xiàn)在來到Powershell,天堂啊!字符串可以非常方便地重復(fù)、拼接、拆分、各種比較,甚至匹配正則表達(dá)式,因為這些都是.NET中的String對象所具有的能力。
- # 輸出20個減號
- "-" * 20
- # 拼接為Powershell
- "Powerh" + "shell"
- # 匹配,以下均返回True
- "powershell" -eq "POWERSHELL"
- "powershell" -like "power*"
- "powershell" -match "shell"
- "powershell".startsWith("p")
- # 區(qū)分大小寫的比較和匹配,以下均返回False
- "powershell" -ceq "POWERSHELL"
- "powershell" -clike "P*"
- "powershell".startsWith("P")
差點忘了偉大的String.Format,格式化字符串,直接看療效:
- # 輸出00EA
- "{0:X4}" -f 234
- # 上述語句等效于
- [string]::format("{0:X4}", 234)
CMD當(dāng)然提供了控制流程控制,因為它提供了IF命令和FOR命令。IF命令很簡單,它的方便性完全取決于條件表達(dá)式是否方便,從這一點一說,CMD的IF語句很好很強(qiáng)大。雖然沒有SWITCH語句是個遺憾,但至少很多個IF語句是完全撐得起場面的。但話說回來,要FOR語句撐起循環(huán)的一片天,還真有點吃力,所以才經(jīng)常會有通過GOTO語句來模擬循環(huán)的情況發(fā)生。
來到Powershell中,說起流程控制,那簡直就是一個飛躍。
從條件分支控制來說,if和switch當(dāng)然一個都不能少,而switch,更是非常的強(qiáng)大。switch精確匹配數(shù)值,這是常規(guī)功能;它還能匹配條件表達(dá)式,這似乎有點讓人驚喜了;它還可以按區(qū)分大小寫和不區(qū)分大小寫兩種方式匹配字符串;不止這些,它還可以按通配符進(jìn)行匹配;都到這一步了,那按正則表達(dá)式匹配也少不掉啦!
說起Powershell的循環(huán),那就更是多樣了,光Foreach都有兩種,一種是Foreach關(guān)鍵字,用于數(shù)組地遍歷;另一種是Foreach-Object命令,用于遍歷管道輸出的多個對象。很巧……也許是Microsoft故意的,F(xiàn)oreach-Object有個別名,就叫Foreach。另外,F(xiàn)or語句當(dāng)然不會少,還有常見的Do...While和While {}兩種循環(huán)。這些都很覺,最神奇的,是Switch語句也可以用于循環(huán)處理數(shù)組,并且根據(jù)數(shù)組中各項的匹配情況來進(jìn)行不同的處理——就相當(dāng)于是把內(nèi)嵌Switch的For/Foreach結(jié)構(gòu)簡化了一樣!來個例子
- $array = 1..5
- switch ($array) {
- {$_ % 2} { "$_ is odd" }
- default { "$_ is even" }
- }
- # 輸出如下
- # 1 is odd
- # 2 is even
- # 3 is odd
- # 4 is even
- # 5 is odd
CMD有函數(shù)嗎?沒有。CMD只是通過CALL模擬了函數(shù)調(diào)用。在腳本內(nèi)部,“CALL:標(biāo)簽”和GOTO:EOF可以模擬函數(shù)調(diào)用及返回。而在腳本外部,則通過“CALL 腳本.CMD”來實現(xiàn)。所以CMD的函數(shù)庫,是一堆.CMD或者.BAT文件。
Powershell當(dāng)然是支持定義函數(shù)的——通過function關(guān)鍵字。而且如果把若干函數(shù)放在一個.ps1腳本文本中,再通過點號(.)來調(diào)用的話,這些函數(shù)立即對當(dāng)前環(huán)境可用——也就是說,這個.ps1腳本文件就是函數(shù)庫。將相關(guān)的函數(shù)組織在一個腳本文件中當(dāng)然會比組織在N個腳本文件中方便得多,也更利于發(fā)布。
當(dāng)然,關(guān)鍵問題在于Powershell支持函數(shù)。Powershell的函數(shù)也可以像腳本文件一樣定義參數(shù)列表、命名參數(shù)和switch參數(shù),并提供極其方便的解析功能。除此之外,Powershell的函數(shù)可以有返回值。不僅有,而且很強(qiáng)大,它可以以數(shù)組的的方式返回多個值。就像這樣
- # 定義函數(shù)
- function test([string] $a, [string] $b) {
- $a.toUpper();
- $b.toLower();
- }
- # 調(diào)用
- $x, $y = test("James", "Fancy")
- "`$x = $x, `$y = $y"
- # 輸出 $x = JAMES, $y = fancy
Powershell的好處遠(yuǎn)不止上面所說的那些,難怪Microsoft這么強(qiáng)烈的推薦使用Powershell代替CMD。甚至有人認(rèn)為Powershell將成為Windows腳本的霸主。它的確比CMD強(qiáng)大了不止一點點,就是相對于WSH來說,它的便捷性和強(qiáng)大的.NET支持也是WSH所不能比擬的——壓根就不是一個數(shù)量級的東西。
更多建議: