引子
一直很羡慕Linux的命令提示符(当然他们叫Shell)。正则表达式,管道,各种神奇的命令,组合起来就能高效完成很多复杂的任务。效率实在是高。流了n年的哈喇子以后,终于有幸用上了Win7,邂逅了cmd的升级版:Windows PowerShell。从此暗爽无比,原来Windows下也有这样的利器呀~
看看下面的Windows脚本,不到15行有效代码。在Win7下只要右击脚本文件,选择Run with PowerShell,就会自动找到最占内存的10个进程,然后将它们占用的内存画成一个三维饼图,如下图所示。

# create new excel instance
$objExcel = New-Object -comobject Excel.Application
$objExcel.Visible = $True
$objWorkbook = $objExcel.Workbooks.Add()
$objWorksheet = $objWorkbook.Worksheets.Item(1)
# write information to the excel file
$i = 0
$first10 = (ps | sort ws -Descending | select -first 10)
$first10 | foreach -Process {$i++; $objWorksheet.Cells.Item($i,1) = $_.name; $objWorksheet.Cells.Item($i,2) = $_.ws}
$otherMem = (ps | measure ws -s).Sum - ($first10 | measure ws -s).Sum
$objWorksheet.Cells.Item(11,1) = "Others"; $objWorksheet.Cells.Item(11,2) = $otherMem
# draw the pie chart
$objCharts = $objWorksheet.ChartObjects()
$objChart = $objCharts.Add(0, 0, 500, 300)
$objChart.Chart.SetSourceData($objWorksheet.range("A1:B11"), 2)
$objChart.Chart.ChartType = 70
$objChart.Chart.ApplyDataLabels(5)
</div>
(1. 这个脚本调用了Excel的COM库。 2. 当然从命令耦合的角度来看,输出成文本格式更有利,但这个例子主要想说明PowerShell的强大以及微软产品优异的复用性。 3. 要手动启动PowerShell,可以在开始菜单的搜索框中直接键入PowerShell回车即可)
简单领略PowerShell的强大之后,下文就从几个方面介绍一下PowerShell相对于以往版本的命令提示符甚至Linux Shell的优势。

Cmdlet + Regex + Pipeline + ...
以往cmd相对于Shell有很多不足,比如命令偏少,部分命令功能偏弱,对正则表达式不支持等等。但现在PowerShell一下赶上来不少。2.0 RTM版内建支持414个命令(术语称为cmdlet),支持正则表达式,强大的管道应用(其实管道本身的功能和以前差不多,关键是冒出来一堆能用管道的命令,比如more, sort, foreach等等),和系统的联系也比以前紧密了很多。
举几个例子来说明:
dir registry::HKEY_CURRENT_USER可以直接显示注册表相应位置的内容,可以看到dir的功能改进了不少。
ps | sort ws -Descending | select -first 10可以显示占用内存最大的10个进程,可以看到管道的灵活应用。
dir -Name | ? {$_ -match "(?<num>.).*(\k<num>)"}可以显示出当前目录下文件名有重复字符的文件。比如abcda.efg,而abcd.efg则不会显示出来。可以看到PowerShell对正则表达式的支持相当强大。(确切的说严格的正则表达式 已经无法实现这样的效果,需要上下文无关文法 才能够支持。)
以前为了演示Linux Shell的强大,Stephenjy发了一个自己的截图,在遇见PowerShell前觉得好神奇,所幸现在也可以实现了。:-)


(为了节约显示空间,PowerShell的部分显示结果被删除,但这个Prompt效果可以用以下脚本验证: function prompt {"($env:username)-($env:computername)-(`$?: $?)-(jobs: $((get-job | measure).Count))-($(get-location))`n(! $(((history)[-1]).ID + 1))->"})
大杀器 - 面向对象
Linux的设计思想决定所有的输入和输出都尽可能是文本格式,这样可以方便各进程间的合作。同样这也要求各个程序提供一定强度的文本解析能力。但Windows的思想与此不同,PowerShell中很多输入输出都不是普通的文本(plain text),而是一个个对象(objects)。因此与其说PowerShell是一种交互环境,不如说它是一种强大语言的Runtime,而这种语言甚至是面向对象的。
比如当键入get-process查看当前进程列表时,系统返回的是这样的列表:
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
318 8 12948 3872 84 1728 AppleMobileD
115 5 13816 13328 38 6920 audiodg
1315 21 11732 10988 108 2544 CcmExec
... ...
</div>
虽然看似一般的格式化文本,但其实这是一个数组,而每个数组元素又是Process类型的对象。同.NET一脉相承,PowerShell中的所有的类都继承自Object,且支持GetType()函数。因此我们可以执行(get-process).GetType()来看看它的类型:
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
</div>
而数组中每个元素的类型可以用(get-process)[0].GetType()查看:
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False Process System.ComponentM...
</div>
其中面向思想的思想非常明显,类成员,方法,继承都出现了。个人感觉这样的好处并不是指望能用PowerShell写什么大型软件,而是体现在另外两个方面:首先,这样让内置的cmdlet及其数据结构组织清晰

