公司在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,如需转载请自行联系原作者