您好,脚本专家!Excel 使用窍门

Microsoft 脚本专家

上月一直在澳大利亚生活和工作,当地同事的创造力和充沛精力总是让我大开眼界。几天前,那里的一个朋友带我和妻子一起外出就餐。他说餐馆离我们的酒店仅几步之遥。

四十分钟后,我们到达了一个非常漂亮的餐馆,此处可眺望悉尼港,悉尼歌剧院熠熠生辉,宛如嵌入五彩沙滩的一枚贝壳。海港大桥(世界上最长的单跨桥)张开结实的臂膀挽起两侧的半岛。

返回时,40 分钟的徒步之行并非一次短暂的闲庭漫步。而是之前需要 15 分钟热身和伸展运动的日常锻炼。

我之所以喜欢此次澳大利亚之行,原因之一是那里的同事看待问题的视角不同。他们的方法似乎别出心裁,常常与我的常规逻辑相悖。

在脚本编写领域,如果您头脑中抱定“经试用正确”这一方法,就极易变得墨守成规。很多时候,您切实希望看到的就是使用相同的方法获取相同的结果。

以监控为例,通过使用 Windows PowerShell,可轻松地获取本地计算机上进程使用情况的出色快照。利用 Get-Process cmdlet,您会得到条理清晰的输出,如图 1 所示。

fig01.gif

图 1 使用 Get-Process 查看本地进程

Get-Process cmdlet 的结果在大部分场合都非常有用。它们显示了处于打开状态的句柄数量、各种内存占用视图以及 CPU 使用率的快照。当 Windows PowerShell 2.0 推出后,您甚至可以使用 Get-Process 和 –computername 参数从远程计算机检索这一概述信息。既然有这些好处,那么究竟为什么有人会不厌其烦地研究其他产品?

问题在于冗长的数据背后隐藏着大量细节。数据往往隐藏的是更为重要的细节。尽管将来 Windows PowerShell 2.0 会支持 –computername 参数听起来令人振奋,但目前它对网络管理员而言还是海市蜃楼。因此,我们不得不使用 Windows 管理规范 (WMI) 和 Win32_Process WMI 类来监视远程系统并以实用方式显示信息。如果认为 Get-Process 的输出内容非常丰富,请再仔细看看 Win32_Process 的输出(如图 2 所示)。

fig02.gif

图 2 利用 WMI 查看进程

那么,如果网络管理员需要的是易于阅读的内存占用量报告,这可怜的家伙该怎么办呢?这正是您需要打破惯例思考、走出自己的圈子并转用 Excel 实现自动操作的地方。很可能您的计算机上已安装了 Microsoft Office Excel。可能您像我们一样并非专家,但由于它是 Microsoft Office 系统的组件,所以也可以利用它。

自动化 Excel 有多难?实际上非常简单,因为 Microsoft 已创建了专门用于处理 Excel 的自动化模型。程序 ID 是 Excel.Application,它是一个 COM 对象。创建 Excel.Application 对象的实例时,Excel 会默认启动并运行,只是您看不见它。但是,可使用 visible 属性让 Excel 显示出来。

以下代码显示了如何创建 Excel.Application 对象、查询 visible 属性的状态,然后将 visible 属性设为 true:

PS C:\> $excel = New-Object -ComObject Excel.Application
PS C:\> $excel.Visible
False
PS C:\> $excel.Visible = $true

然后,您会看到一个非常奇怪的 Excel 视图,它看起来就像 Excel 应用程序的一个外壳(如图 3 所示)。没有工作簿、没有电子表格—只是光秃秃的 Excel。

fig03.gif

图 3 光秃秃的 Excel—没有工作簿或电子表格

我们需要将一个工作簿添加到应用程序中。为此,我们借助工作簿对象的 add 方法。工作簿对象是从主 Excel.Application 对象进行访问的,正如您在此看到的,我们将工作簿对象存储在一个名为 $workbook 的变量中:

$workbook = $excel.Workbooks.add()

现在,需连接特定的电子表格。默认情况下,工作簿添加到 Excel 时,会向其中添加三个电子表格。这些电子表格可通过数字进行标识。在以下代码行中,连接第一个电子表格并将返回的电子表格对象存储在名为 $sheet 的变量中:

$sheet = $workbook.worksheets.Item(1)

现在,可将数据写入该电子表格。Excel 电子表格中的信息存储在单元格中。由于单元格位于电子表格之中,所以可使用 $sheet 变量中存储的电子表格对象访问特定的单元格。具体方法是使用指代电子表格中行和列的数字。在 Excel 电子表格中,行是数字而列是字母,这一点有些让人迷惑。但使用自动化模型时,行和列均为数字。第一个数字是行,第二个数字是列。只需对特定单元格进行赋值即可写入单元格:

$sheet.cells.item(1,1) = "Test"

向 Excel.Application 对象添加一个工作簿并将数据添加到电子表格中的单元格后,Excel 工作簿将如图 4 所示。

fig04.gif

图 4 向单元格添加值

了解这些内容后,让我们整理出有用的内容。从 WMI 获取进程信息集合,将每个进程的名称和内存占用量写入 Excel 电子表格,然后创建一个图表来突出显示所用内存。这正是 WriteProcessInformationToExcel.ps1 的功能所在。《TechNet 杂志》**网站上有完整的脚本。

脚本首先使用 Get-WmiObject cmdlet 检索有关进程的信息集合。使用 Win32_Process WMI 类获取该信息,并将其存储在 $processes 变量中:

$processes = Get-WmiObject -class Win32_Process

现在,创建 Excel.Application 对象的一个实例并将其存储在变量 $excel 中,然后显示应用程序并添加一个工作簿。通常,决定执行的任意 Excel 自动化都要完成以上步骤。具体代码如下:

$excel = new-object -comobject excel.application
$excel.visible = $true
$workbook = $excel.workbooks.add()

Excel 的一个不足是工作簿始终会创建三个电子表格。我们认为这一点非常浪费,因为我们仅使用一个电子表格。幸运的是,可利用自动化删除多余的电子表格:使用工作表集合连接第三个电子表格并调用 delete 方法。执行相同的操作删除第二个电子表格:

$workbook.workSheets.item(3).delete()
$workbook.WorkSheets.item(2).delete()

接下来,重命名剩余的电子表格。这一点非常重要,因为如果决定使用 ActiveX 数据对象 (ADO) 来查询 Excel 电子表格,将在连接字符串中使用此电子表格名称。因此,要使代码易读且直观,电子表格应具有逻辑名称。要重命名电子表格,只需向特定电子表格的 name 属性赋一个值即可。以下代码将第一个电子表格重命名为 "Processes":

$workbook.WorkSheets.item(1).Name = "Processes"

现在,需连接重命名后的电子表格。使用工作表对象的 Item 方法并将电子表格的名称指定给它:

$sheet = $workbook.WorkSheets.Item("Processes")

电子表格的第一行将包含标头信息。我们将绘制边框并使属性名称显示为粗体。由于数据将从第二行开始,因此我们将值 2 赋给计数器变量 $x:

$x = 2

接下来的四行代码创建四个枚举类型。枚举类型用于告诉 Excel 允许将哪些值填入特定的选项类型。例如,xlLineStyle 枚举用于确定所绘制线条的类型:双线条、虚线等。MSDN 上详细介绍了这些枚举值

为使代码更易于阅读,针对将使用的每个枚举类型创建一个快捷别名。实际上,我们将把代表枚举名称的字符串转换成 [type]。此技术实际是一个非常酷的窍门:

$lineStyle = "microsoft.office.interop.excel.xlLineStyle" -as [type]
$colorIndex = "microsoft.office.interop.excel.xlColorIndex" -as [type]
$borderWeight = "microsoft.office.interop.excel.xlBorderWeight" -as [type]
$chartType = "microsoft.office.interop.excel.xlChartType" -as [type]

现在需格式化第一行。使字体为粗体,将线条定义为 xlDashDot,允许自动指定颜色,并将边框宽度设为中等粗细:

For($b = 1 ; $b -le 2 ; $b++)
{
 $sheet.cells.item(1,$b).font.bold = $true
 $sheet.cells.item(1,$b).borders.LineStyle = $lineStyle::xlDashDot
 $sheet.cells.item(1,$b).borders.ColorIndex = $colorIndex::xlColorIndexAutomatic
 $sheet.cells.item(1,$b).borders.weight = $borderWeight::xlMedium
}

完成后,通过使用 item 方法选择单元格并指定行和列的坐标,向第一行赋值。接下来,使用直接赋值写入列标题:

$sheet.cells.item(1,1) = "Name of Process"
$sheet.cells.item(1,2) = "Working Set Size"

现在,需将 WMI 查询产生的 $processes 变量中存储的进程信息放入适当的单元格中。使用 foreach 语句遍历进程信息集合。将变量 $process 定义为集合枚举器(占位符),并选择将名称和 workingSetSize 属性分别写入第一列和第二列。

$x 变量将在此发挥作用。从第二行开始,在遍历进程集合的同时,递增 $x 变量的值以便它始终指向集合中的当前行。通过以下代码,即可对 $processes 进程信息集合中存储的所有数据进行整理分类:

Foreach($process in $processes)
{
 $sheet.cells.item($x, 1) = $process.name
 $sheet.cells.item($x,2) = $process.workingSetSize
 $x++
} #end foreach

填写完 Excel 电子表格后,我们打算调整列的大小以使单元格与其中所存储数据的尺寸相同。为此,可通过指定要使用的列坐标来创建一个范围;但是,也可以只使用电子表格的 usedRange 属性。创建完范围对象后,选择 EntireColumn 属性并使用 AutoFit 方法调整列的大小。由于该方法始终会返回数据,我们将结果传送给 Out-Null cmdlet。从而避免控制台上布满了杂乱无用的信息。下面是所用的代码:

$range = $sheet.usedRange
$range.EntireColumn.AutoFit() | out-null

至此我们已经完成了任务,此时的电子表格具有所有进程的名称和内存工作集,相当不错。但让我们继续创建一个图表。具体操作非常简单。使用工作簿的 charts 对象的 add 方法。由于该方法还会返回我们并不想要看到的信息,所以将结果传给 Out-Null cmdlet,如下所示:

$workbook.charts.add() | out-null  

以上命令添加了一个直线图。如果想要定义另一类型的图表,需使用一个图表类型枚举值。为此,可使用一个 microsoft.office.interop.excel.xlChartType 枚举值,如 xl3DPieExploded 类型。毫无疑问,xl3DPieExploded 类型将创建一个分裂的三维饼图。将这一枚举类型赋给 ActiveChart 对象的 chartType 属性。然后,将图表的数据源指定为 $range 变量中定义的范围。结果是线图闪烁一下,然后 3D 饼图在屏幕上爆炸开来。以下是相关代码:

$workbook.ActiveChart.chartType = $chartType::xl3DPieExploded
$workbook.ActiveChart.SetSourceData($range)

在玩耍时,我们想要旋转饼图。具体实现方法是使用 ActiveChart 对象的 rotation 属性。使用 for 语句以 15 为增量将其数量逐渐增加到 360。360 是圆圈的度数;图表将以每次 15 度的增量旋转一整圈。实际视觉效果相当酷。以下是实现这一功能的代码:

For($i = 1 ; $i -le 360 ; $i +=15)
{
 $workbook.ActiveChart.rotation = $i
}

最后需要做的一件事是保存电子表格。为此,使用 Test-Path cmdlet 查看电子表格是否已存在。如已存在,使用 Remove-Item cmdlet 删除旧的电子表格,然后将当前的工作簿保存到 $strPath 变量中存储的位置。使用 Excel.Application 对象的 ActiveWorkbook 对象以及 SaveAs 方法保存工作簿。如果并未保存有电子表格的副本,使用 ActiveWorkbook 对象的 SaveAs 方法并直接保存:

IF(Test-Path $strPath)
  { 
   Remove-Item $strPath
   $Excel.ActiveWorkbook.SaveAs($strPath)
  }
ELSE
  {
   $Excel.ActiveWorkbook.SaveAs($strPath)
  }

运行脚本时,您将看到如图 5 所示的图表。

fig05.gif

图 5 按进程分裂的饼图

电子表格本身位于 Processes 选项卡。图 6 显示了列标题、针对边框选择的点划线样式以及粗体列标题。进程名称和工作集大小属性是我们显示的两列数据。

fig06.gif

图 6 完成后的电子表格

正如您所看到的,通过使用 Excel.Application 自动化模型,即可利用这一功能丰富且强大的应用程序的分析和图表工具来处理服务器的数据。

Ed Wilson 是 Microsoft 的高级顾问,也是知名的脚本专家。他还是 Microsoft 认证培训师,为世界各地的 Microsoft Premier 客户组织召开了广受欢迎的 Windows PowerShell 研讨会。他自己撰写了八部著作,其中有多本探讨了 Windows 脚本,并与人合著了十余部其他书籍。Ed 拥有 20 多个行业证书。

Craig Liebendorfer 是语言艺术家,也是 Microsoft Web 的资深编辑。Craig 一直无法相信他可以每天靠舞文弄墨来维持生计。无厘头式的幽默是他的最爱之一,因此他应该非常适合这个工作。Craig 认为美丽动人的女儿是自己一生最大的成就。