公司在Windows環境下進行開發,是以在寫自動建構的時候,自然而然地想到了CMD SHELL。本來考慮過使用Windows Script Host腳本(WSF、JS或VBS)來寫,但要在WSH腳本裡調用VS的批處理來設定環境很困難。随着項目結構變得複雜,CMD SHELL寫的建構腳本也開始變得複雜,這個時候就感到CMD SHELL有點吃力了,于是想到了Powershell。
感謝Tobias Weltner博士,以及他那本免費的《Master Powershell》!
看了《Master Powershell》一遍之後,我覺得學習Powershell是正确的。
CMD SHELL(以下簡稱CMD)來源于DOS時代的批處理腳本,它最初的設計就是為着順序批處理來的。在Windows 2000和Windows XP時代,DOS批處理正式更新為CMD SHELL,在文法和功能上做了一些擴充;腳本檔案開始支援.CMD擴充名,并相容之前的.BAT擴充名;其名稱也由“DOS視窗”改為“指令提示符”。CMD SHELL雖然可以完成很多比較複雜的任務,但卻非常考驗腳本開發者大腦的堆棧大小。
CMD的确适合寫一些簡單的SHELL程式,而Powershell卻包含了寫一個複雜的腳本程式所需要的各種支援,包括文法和函數庫——巨大的.NET函數庫。
CMD中處理參數,通常是按順序處理%1-%9,參數比較多還需要通過SHIFT指令來對參數進行移位。如果想處理位置不定的switch參數,或者處理命名參數,那就分析參數這部分腳本都能把人搞攪暈。而Powershell原生就支援參數數組、命名參數和switch參數。比如
# test.ps1
# 在腳本中定義命名參數和switch參數
param($name, [switch] $isMale)
# 如果 .\test.ps1 -name "James Fancy" -ismale
# $name的值為James Fancy
# $isMale的值為True
# 如果預設-ismale參數,$isMale的值為False
# 如果 .\test.ps1 -name -ismale
# 會直接報告錯誤,因為-name需要附加的參數
可見Powershell已經将參數處理部分進行了很好的封閉。如果要用CMD來寫,那就需要GOTO、SHIFT若幹條件分支以及若幹臨時的環境變量。
CMD中的變量,其實都是環境變量,而且其值一定是字元串。而Powershell中有真正意義的變量,并且這些變量可以是字元串類型、數值類型、日期類型……甚至任意對象類型,隻要是.NET庫中支援的對象。不錯,Powershell是一個面向對象的腳本。很酷,是麼?
CMD中如果需要清單怎麼辦——用分号分隔的字元串值;那如果需要哈希表呢——用分号分隔的帶等号的字元串值……是的,CMD可以做到,隻是處理起來麻煩一點而已。當然,在Powershell中不需要這麼麻煩,Powershell支援數組類型的變量和哈希表類型的變量,就像——嗯,像什麼呢?有點像PHP,也有點像Javascript。
# 定義一個數組
$a = 1,2,3
# 也可以這樣定義
$a = @(1,2,3)
# 或者定義一個空數組
$a = @()
# 再定義兩個哈希表
$m1 = ${ key1="value1";key2=1234 }
$emptymap = ${}
在CMD中如果想計算四則運算,需要用到SET /A指令,可以進行常見的各種算術運算。Powershell當然不輸于CMD。Powershell中可以進行各種各樣的運算,而且完全不需要通過指令來進行。
當然Powershell能做的不僅是這樣。比如擷取日期,CMD下需要擷取日期當然是用DATE指令,如果要幹淨一點的日期,用DATE /T,不過輸出的日期格式嘛……當然就看在Windows裡怎麼設定的啦。而在Powershell裡,日期是一個對象,格式嘛,當然可以想什麼樣就什麼樣……
# 按兩種格式輸出日期
(get-date).toString("yyyy-MM-dd HH:mm:ss")
# 輸出 2011-10-04 09:54:47
(get-date).toString("yyyy年M月d日")
# 輸出 2011年10月4日
對了,還有條件表達式,Powershell是通過-eq、-ne、-lt、-gt等運算符來進行比較,還可以通過-and、-or等運算符來表達組合條件……差點忘了-not,當然它還可以簡寫成“!”。
# 下面表達式傳回True
(1 -lt 2) -and (3 -eq 03)
CMD中想要處理字元串,那簡直就是惡夢!雖然SET和FOR指令外加GOTO或者CALL指令可以對字元串進行一些簡單的處理,但是處理起來那是真的太太太複雜了。現在來到Powershell,天堂啊!字元串可以非常友善地重複、拼接、拆分、各種比較,甚至比對正規表達式,因為這些都是.NET中的String對象所具有的能力。
# 輸出20個減号
"-" * 20
# 拼接為Powershell
"Powerh" + "shell"
# 比對,以下均傳回True
"powershell" -eq "POWERSHELL"
"powershell" -like "power*"
"powershell" -match "shell"
"powershell".startsWith("p")
# 區分大小寫的比較和比對,以下均傳回False
"powershell" -ceq "POWERSHELL"
"powershell" -clike "P*"
"powershell".startsWith("P")
差點忘了偉大的String.Format,格式化字元串,直接看療效:
# 輸出00EA
"{0:X4}" -f 234
# 上述語句等效于
[string]::format("{0:X4}", 234)
CMD當然提供了控制流程控制,因為它提供了IF指令和FOR指令。IF指令很簡單,它的友善性完全取決于條件表達式是否友善,從這一點一說,CMD的IF語句很好很強大。雖然沒有SWITCH語句是個遺憾,但至少很多個IF語句是完全撐得起場面的。但話說回來,要FOR語句撐起循環的一片天,還真有點吃力,是以才經常會有通過GOTO語句來模拟循環的情況發生。
來到Powershell中,說起流程控制,那簡直就是一個飛躍。
從條件分支控制來說,if和switch當然一個都不能少,而switch,更是非常的強大。switch精确比對數值,這是正常功能;它還能比對條件表達式,這似乎有點讓人驚喜了;它還可以按區分大小寫和不區分大小寫兩種方式比對字元串;不止這些,它還可以按通配符進行比對;都到這一步了,那按正規表達式比對也少不掉啦!
說起Powershell的循環,那就更是多樣了,光Foreach都有兩種,一種是Foreach關鍵字,用于數組地周遊;另一種是Foreach-Object指令,用于周遊管道輸出的多個對象。很巧……也許是Microsoft故意的,Foreach-Object有個别名,就叫Foreach。另外,For語句當然不會少,還有常見的Do...While和While {}兩種循環。這些都很覺,最神奇的,是Switch語句也可以用于循環處理數組,并且根據數組中各項的比對情況來進行不同的處理——就相當于是把内嵌Switch的For/Foreach結構簡化了一樣!來個例子
$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有函數嗎?沒有。CMD隻是通過CALL模拟了函數調用。在腳本内部,“CALL:标簽”和GOTO:EOF可以模拟函數調用及傳回。而在腳本外部,則通過“CALL 腳本.CMD”來實作。是以CMD的函數庫,是一堆.CMD或者.BAT檔案。
Powershell當然是支援定義函數的——通過function關鍵字。而且如果把若幹函數放在一個.ps1腳本文本中,再通過點号(.)來調用的話,這些函數立即對目前環境可用——也就是說,這個.ps1腳本檔案就是函數庫。将相關的函數組織在一個腳本檔案中當然會比組織在N個腳本檔案中友善得多,也更利于釋出。
當然,關鍵問題在于Powershell支援函數。Powershell的函數也可以像腳本檔案一樣定義參數清單、命名參數和switch參數,并提供極其友善的解析功能。除此之外,Powershell的函數可以有傳回值。不僅有,而且很強大,它可以以數組的的方式傳回多個值。就像這樣
# 定義函數
function test([string] $a, [string] $b) {
$a.toUpper();
$b.toLower();
# 調用
$x, $y = test("James", "Fancy")
"`$x = $x, `$y = $y"
# 輸出 $x = JAMES, $y = fancy
Powershell的好處遠不止上面所說的那些,難怪Microsoft這麼強烈的推薦使用Powershell代替CMD。甚至有人認為Powershell将成為Windows腳本的霸主。它的确比CMD強大了不止一點點,就是相對于WSH來說,它的便捷性和強大的.NET支援也是WSH所不能比拟的——壓根就不是一個數量級的東西。
本文轉自邊城__ 51CTO部落格,原文連結:http://blog.51cto.com/jamesfancy/678945,如需轉載請自行聯系原作者