Windows PowerShell简单命令。强大的管理能力

Don Jones

此专栏基于 Windows PowerShell 的预发布版本。文中的所有信息均可能会发生变更。

在经历了漫长的等待之后,Windows PowerShell 终于已蓄势待发。这意味着该是 Windows 管理员提起注意的时候了。Windows PowerShell 提供了也许是最简单且最灵活的方法来自动执行各种各样的管理任务,从而您的工作效率和效力都得到提高。

但更为重要的是,Microsoft 正在 Windows PowerShell™ 之上构建 Exchange Server 2007 和 System Center 2007 之类产品的图形管理控制台。这意味着您将能够从 Windows PowerShell 内部执行几乎所有的管理任务。随着时间的推移,Microsoft 计划提高越来越多产品的管理能力。因此,Windows PowerShell 最终会成为用于管理几乎任何 Microsoft 服务器产品的第一个全能型工具。为了帮助您尽快入门,我将在此新设的专栏中定期探讨 Windows PowerShell。请务必要下载一份软件

强大而又简便的功能

顾名思义,Windows PowerShell 是一个外壳,但与从 Windows NT® 3.1 起就出现的命令提示符 (Cmd.exe) 不同。Cmd.exe 不会消失,但随着 Windows PowerShell 的到来,几乎没有什么理由再继续使用 Cmd.exe。

Windows PowerShell 在使用方面与 Cmd.exe 并无多大不同,只是 Windows PowerShell 的功能更为强大。与 Cmd.exe 一样,Windows PowerShell 具有内置的脚本编写语言,不过它比 Cmd.exe 原始的批处理语言更为灵活。灵活性是如何体现的呢?有了 Windows PowerShell,您可以使用仅包括大约半打内置关键字的语言自动执行极其复杂的任务。

既然我已提到了脚本编写,那么现在我可能就应该简单提一下安全性。Windows PowerShell 得益于 Microsoft 过去十多年在安全性方面所取得的研究成果。默认情况下,Windows PowerShell 不会运行脚本,只能交互式地用它来运行单个命令。如果您确实启用了脚本编写,则可令 Windows PowerShell 仅运行经过数字签名的脚本。这些均有助于确保 Windows PowerShell 不会成为下一个 VBScript — 一种伟大的语言,但它常被滥用来创建恶意脚本。VBScript 也不会退出历史舞台,但您可能会发现 Windows PowerShell 对于许多不同的任务来说更易于使用。

Cmd.exe 做到的事情,Windows PowerShell 几乎都能做到。例如,您可以运行 ipconfig 并且会获得同样熟悉的输出。但是 Windows PowerShell 会引入一整套新的命令,它们不是外部可执行文件。这些 cmdlet(发音为“command-let”)就内置于 Windows PowerShell 之中(要了解对 Windows PowerShell 使用入门最有用的 cmdlet,请参见侧栏的“快速使用入门十大 Cmdlet”)。

快速使用入门十大 Cmdlet

  • Get-Command 用于检索所有可用 cmdlet 的列表。
  • Get-Help 用于显示有关 cmdlet 和概念的帮助信息。
  • Get-WMIObject 用于通过 WMI 来检索管理信息。
  • Get-EventLog 用于检索 Windows 事件日志。
  • Get-Process 用于检索单个活动进程或活动进程的列表。
  • Get-Service 用于检索 Windows 服务。
  • Get-Content 用于读入文本文件,将每行视为一个子对象。
  • Add-Content 用于将内容附加到文本文件。
  • Copy-Item 用于复制文件、文件夹和其他对象。
  • Get-Acl 用于检索访问控制列表 (ACL)。

要获得 Windows PowerShell 自带的完整 cmdlet 列表,请访问 windowssdk.msdn.microsoft.com/en-us/library/ms714408.aspx

所有 cmdlet 都以标准的“动词-名词”格式命名,这使其易于理解和记忆。例如,运行 Get-Command cmdlet 将会列出所有可用的 cmdlet。对于管理员来说最有用的 cmdlet 也许就是 Get-WMIObject。若您想要查明 Server2 正在运行哪个服务包,只需运行:

Get-WMIObject Win32_OperatingSystem –Property ServicePackMajorVersion
 –Computer Server2

要使用 VBScript 来发现同一信息,就得编写几行代码。利用 cmdlet 可以处理服务(Start-Service、Stop-Service 等)、进程(Stop-Process 等)、文件(例如,Rename-Item、Copy-Item、Remove-Item、Move-Item)等等。其中许多 cmdlet 甚至还有快捷名称,称为别名。对于 Get-WMIObject 而言,您可以只键入 gwmi。运行 Get-Alias 将为您提供这些快捷名称的列表。

为什么面向对象很重要

Windows PowerShell 在 Microsoft® .NET Framework 上构建,所以它完全是面向对象的。通常,只有软件开发人员才会对此感到兴奋,但是就本处的情况而言,面向对象可以为管理员节省大量的时间。这是因为管理员现在只需在基于文本的外壳内就能直接处理丰富的对象。请看以下示例:

Get-Process | Sort-Object pm –desc | Select-Object –first 10

这里只有一行,其中含有以管道分隔的三个不同的 cmdlet(稍后会对此进行详述)。第一个 cmdlet 检索所有正在运行的进程,然后将那些对象传递给或通过管道输送到 Sort-Object。第二个 cmdlet 基于每个进程对象的 pm(即物理内存)属性按降序对其进行排序。然后将进程对象的有序集合通过管道输送到 Select-Object,它将选取前十个对象进行显示。结果呢?此行简单命令会显示机器上的十大物理内存使用者,如图 1 所示。这是一种极其有效的方法,借此可在执行某些故障排除时进行快速浏览。

图 1 使用简单的 Cmdlet 进行故障排除

图 1** 使用简单的 Cmdlet 进行故障排除 **(单击该图像获得较大视图)

管道(在美式键盘上,该竖线字符通常位于反斜线按键上)的使用是 Windows PowerShell 具有如此能力不可或缺的组成部分。使用此字符,可以将对象从一个 cmdlet 传递(或通过管道输送)给另一个,从而使每个 cmdlet 可以进一步提炼结果、设置结果的显示格式,等等。之所以能够采用这种机制,是因为每个 cmdlet 均会返回一个或多个对象,而非纯文本,这就使得后续 cmdlet 可以处理完整的对象。

Windows PowerShell 中的对象使用非常普遍,这全都仰仗于它的变量。而且,您不必预先声明变量,只需在变量名称前放置一个美元符号 ($) 就可以开始使用它们了。尽管不是必需的,但您也可以将所要放入变量的数据类型告知 Windows PowerShell。这使得 Windows PowerShell 可以将变量映射到某一种极其强大的 .NET Framework 类型,从而为您提供诸多附加的内置功能。例如,假设您想要提示输入计算机名称并从该计算机中检索服务包版本,但是您不知道键入的计算机名称是否会包括两个反斜线(如 \\Server2)。由于您知道 Get-WMIObject cmdlet 不需要反斜线,因此您可以将计算机名称保存到一个字符串变量中,然后使用 Replace 方法以空字符串替换反斜线,如下所示:

[string]$c = Read-Host "Enter computer name"
$c = $c.Replace("\","")
    Get-WMIObject Win32_OperatingSystem
    –Property  ServicePackMajorVersion
    –Computer $c

已在 $c 变量中提供了 –Computer 参数的值。该变量最初是作为字符串而创建的,因此它具有 .NET Framework 字符串类型的所有功能,包括 Replace 方法。当然,要了解所有这些功能需要花费一些时间,但是通过示例您应该很容易就能掌握这些功能。Windows PowerShell 本身也有助于简化学习过程。例如,如果您键入 $c = $c.(不要忘了句号)并按 Tab,Windows PowerShell 将显示 Clone(),这是字符串类型的第一个方法。如果您一直按 Tab,Windows PowerShell 将会循环列出所有可用的方法。实质上,当您这样做时,Windows PowerShell 是在向您展示它所知道的字符串处理方法。

在此给出一项更难的任务。从文件中读取计算机名称的列表(每行一个名称)并显示每台计算机的服务包号。在 VBScript 中,此项任务将需要一打或更多行的代码。在 Cmd.exe 中,您就得使用复杂的批处理文件。在 Windows PowerShell 中,此项任务仅需一行:

Get-Content "c:\computers.txt" | foreach  
{ $_; gwmi Win32_OperatingSystem -prop 
ServicePackMajorVersion -comp $_ }

Get-Content cmdlet 读取 C:\Computers.txt 的内容。文件的每一行均自成一个对象。此对象集合(即,计算机名称)通过管道输送到 foreach 命令(它其实就是 ForEach-Object cmdlet 的别名)。花括号内的命令会对通过管道送入的每个对象重复一次(在本例中就意味着它们对每个计算机名称都要运行一次)。特殊的 $_ 变量将包含当前对象(即,当前的计算机名称)。

花括号内实际是两个命令:第一个只是通过输出 $_ 的内容来显示当前的计算机名称。第二个是现已熟悉的 gwmi。结果将显示文件中所列出的每台计算机的服务包版本列表。所有这些都是用相对简单的一行命令完成的。

请注意,–Property 和 –Computer 参数名已被缩写。Windows PowerShell 仅需有足够的信息即可唯一地区分参数名。

可读性与复用

然而,编写一行命令和参数无助于可读性。Windows PowerShell 允许您将其拆分成更具可读性的形式,您甚至不用编写脚本就能将其直接键入外壳。这就是它可能的形式:

PS C:\> $names = get-content "c:\computers.txt"
PS C:\> foreach ($name in $names) {
>> $name
>> gwmi Win32_OperatingSystem -prop ServicePackMajorVersion -comp $name
>> }
>>

这一次,文件的内容存储在变量 $names 中。本例仍使用了 foreach,但它不是通过管道进行输入的,因此必须告知它需要循环处理哪个对象集合以及将每个对象存储在哪个变量中,即 ($name in $names) 所指明的那一部分。其他方面大体相同,只要您一按 Enter,就会执行代码并显示结果。

如果要重复使用此同一代码,只需将其制成函数即可。再次将以下内容直接键入到外壳中:

PS C:\> function Get-ServicePacks ($file) {
>> $names = get-content $file
>> foreach ($name in $names) {
>> $name
>> gwmi win32_operatingsystem -prop servicepackmajorversion -comp $name
>> }
>> }
>>

如您所见,实际上并未做太多更改。只需将上一示例封入一个名为 Get-ServicePacks(与 Windows PowerShell 的“动词-名词”命名惯例保持一致)的函数即可。该函数现在具有一个名为 $file 的输入参数,该参数在 Get-Content cmdlet 中已被取代,这样便可在运行函数时指定另一不同的文件。至此已定义了函数,运行它很简单,方法几乎与 cmdlet 一样,只需调用其名称并传递输入参数即可:

PS C:\> Get-ServicePacks c:\computers.txt

图 2 显示了结果。

其缺点在于此函数仅在该 Windows PowerShell 实例运行期间才存在。一旦关闭外壳,该函数即会消失。您可以将该函数复制到您的 Windows PowerShell 配置文件中,该配置文件是一种自动运行的脚本,它会在 Windows PowerShell 每次启动时执行。这样做就使该函数在打开的每个 Windows PowerShell 窗口中均可用。或者,如果需要,也可将该函数做成独立的脚本,然后只需键入其路径和文件名即可执行该脚本。

图 2 运行 Get-ServicePacks 函数的结果

图 2** 运行 Get-ServicePacks 函数的结果 **

一切尽在于文件(或文件夹)

Windows PowerShell 决不只有函数和 cmdlet。让我们以文件管理为例来快速了解一下其中还蕴藏着什么别的内容。您可能对 Cmd.exe 中的驱动器和文件夹导航再熟悉不过了 — 键入 C: 可切换到 C 驱动器,键入 cd \test 可转入 C:\Test 文件夹。Windows PowerShell 的工作方式完全相同,不过 cd 只是 Set-Location cmdlet 的别名。

尝试运行 Get-PSDrive,该 cmdlet 会列出所有可用的驱动器。除了常用的 C:、D:(也许还有 A:)驱动器之外,您还会发现一个名为 Cert 的驱动器、另一个名为 Env 的驱动器以及其他名为 HKCU 和 HKLM 的驱动器。实际上,Windows PowerShell 将许多不同类型的存储资源均以“驱动器”形式公开,从而使得诸如本地证书存储区、环境变量和注册表等资源均可通过一个象文件那样为大家所熟悉的导航界面来获得。

键入 Set-Location HKLM:(或 cd hklm:,如果您喜欢使用快捷方式)而后按 Enter,可转到 HKEY_LOCAL_MACHINE 注册表配置单元。如果要删除某个表项,可使用 del 将其删除,就像该表项是文件或文件夹一样(不过一定要非常小心,如果删除了必需的表项或是对注册表进行了不正确地修改,可能会发生严重问题)。

这一切灵活性均来自于提供程序,它们将资源(如注册表和证书存储区)映射成类似于文件系统那样的格式。Microsoft 计划通过另外的提供程序来扩展 Windows PowerShell,例如,使您能够导航 Exchange Server 存储区,就像它是文件系统一样。这是一项非常重要的技术,重要性就在于它吸纳了 Windows 所使用的林林总总的存储库并使它们看起来全都相同,而且还使得它们都可以通过您已熟悉的命令和技术系统来进行管理。

安全性设计

我已提到过 Windows PowerShell 在设计时很注重安全保护。Windows PowerShell 中主要的安全功能是它的执行策略。默认情况下,此策略设置为“Restricted”,您可以通过运行 Get-ExecutionPolicy cmdlet 来进行验证。在 Restricted 模式下,不能运行脚本。就这么简单。由于这是默认模式,因此刚出盒的 Windows PowerShell 不能用来运行脚本。

您可以使用 Set-ExecutionPolicy cmdlet 指定其他模式。我个人更偏爱 RemoteSigned 模式。在此模式下,可以运行未经数字签名的本地脚本(而非远程脚本),同时能够以最简单的方法开发和测试脚本。除非已使用受信任发布方所颁发的证书对脚本进行了数字签名,否则 AllSigned 模式不会运行任何脚本。最后,采用 Unrestricted 策略可运行任何脚本。我建议切莫采用此策略,因为以此方式打开的 Windows PowerShell 会运行可能伺机找上您计算机的恶意脚本。请注意,执行策略也可能受组策略支配,它会替代所有本地设置(如果组策略设置正要替代您的本地设置,Set-ExecutionPolicy 会向您发出警告)。

另外,Windows PowerShell 不会从当前目录运行脚本,除非您指定了该路径。这样设计是为了防止命令攻击。比如,某人创建了一个名为 IPConfig.ps1 的脚本(PS1 是 Windows PowerShell 脚本文件的文件扩展名)。如果文件可在当前文件夹之外运行,则您可能会键入 ipconfig 而运行此用户所创建的脚本,可当时您其实是希望运行 Windows 程序 Ipconfig.exe,这样就带来了风险。由于 Windows PowerShell 不会在当前文件夹之外运行脚本,所以这种错误不可能发生。如果您的确想在当前文件夹之外运行脚本,只需指定路径即可:例如 .\myscript。对当前文件夹的显式引用确保您知道自己正在运行脚本,而非外壳命令。

Windows PowerShell 还具有使实验变得更加安全的功能。例如,请考虑(但请勿尝试**)这个令人害怕的组合:

Get-Process | Stop-Process

Get-Process cmdlet 会创建进程对象的集合并通过管道将其输送到 Stop-Process cmdlet,而后者真的会将它们停止!这会导致在大约 5 秒钟后出现蓝屏 STOP 错误,原因是终止了关键的 Windows 进程。但是,您可以通过添加非常方便的 –Whatif 参数来看看将会发生什么情况,而不会令其真的发生:

Get-process | Stop-Process -Whatif

在 Windows PowerShell 中运行此命令会产生一组语句,它们会告诉您 cmdlet 将会做什么,而不会真的让它们这样做。Windows PowerShell 中的在线帮助系统(可通过 help 别名访问)尚未记载 –Whatif 参数,但请记住它。它是一个很好的工具,用于测试脚本和 cmdlet 以检验其结果,而不会实际做出任何具有潜在危害性和破坏性的事情。

总结

在没有加入此 Windows PowerShell 版本的功能中,最重要的也许就是对 Active Directory® 服务接口 (ADSI) 的支持。尽管 Windows PowerShell 可以利用与 Active Directory 和其他目录服务配合工作的功能非常强大的 .NET 类,但它尚没有一个方便的 Get-ADSIObject cmdlet。这就给处理目录对象带来一点困难。

此外,Windows PowerShell 通常提供了多种不同的方法来执行同一任务。这样很好,但可能会令人在学习 Windows PowerShell 时变得更加迷惑,因为对于任何给定的任务,您可能都会看到半打不同的处理方法示例。

随着时间的推移,一切都会得到解决,Windows PowerShell 团队将会继续为产品增添更多的特色和功能。要想一直成为圈内人,请访问该团队的博客

Don JonesScriptingAnswers.com 的创始人,《Windows PowerShell:TFM》(SAPIEN Press,2006)的合著者。请通过 don@scriptinganswers.com 与他联系。

© 2008 Microsoft Corporation 与 CMP Media, LLC.保留所有权利;不得对全文或部分内容进行复制.