Windows PowerShellWindows PowerShell 构造

Don Jones

上个月,我向您展示了将 Windows PowerShell 用于即时解决管理任务的方法 - 但没有编写任何文字资料。但是,Windows PowerShell 是一种优秀的交互式命令解释程序,您可以在开始利用它强大但脚本简单的语言时使用它的功能并自动执行更加复杂的任务。

但首先您要问问自己:Microsoft 真的需要另一种脚本语言吗?毕竟,Microsoft 向我们提供了 KiXtart(一种登录脚本处理器)以及 Visual Basic® Scripting Edition (VBScript)。但是,答案是肯定的。Microsoft 确实需要另一种脚本语言。我将解释原因。

Windows PowerShell™ 语言需要简单直观,这样管理员可以不经过太多训练就掌握它。它也需要相当灵活,这样就可以适应 Windows PowerShell 本身提供给用户的所有强大功能。

因为 Windows PowerShell 是基于 Microsoft® .NET Framework 的,所以它的脚本语法需要得到 .NET 的支持。在编写 Windows PowerShell 脚本语言时,设计师选择的实质上是高度简化的 C#(读作 C-Sharp,是 .NET Framework 附带的语言之一)。为什么不继续使用类似于 VBScript 的语法呢?实际上,Windows PowerShell 脚本语言与 VBScript 并没有多大不同,只是与 C# 的语法更加接近,从某种意义上讲,PowerShell 是学习 .NET Framework 编程的踏脚石。如果您决定进阶到 Visual Studio® 并开始编写 C# 应用程序,您的大部分 Windows PowerShell 脚本语法知识也将继续给您提供帮助。

Windows PowerShell 脚本语言 - 或者就此而言,任何脚本语言 - 中最重要的一点是它的构造。 这些是特殊语言元素,允许 Windows PowerShell 执行逻辑比较并根据比较结果进行不同的操作或者允许不断地重复指令。

进行逻辑思考

逻辑比较是很多脚本语言构造的核心,Windows PowerShell 也不例外。比较实质上查看两个值或对象并计算以决定比较结果是 True 还是 False。例如,您可以问自己,“用户的密码有效期是否到今天截止?”如果今天到期结果为 True,否则为 False。注意我将 True 和 False 的首字母写成大写形式,因为在 Windows PowerShell 中它们是具有特殊含义的词。

此处是一个您可以在 Windows PowerShell 中执行的真实的逻辑比较示例:

PS C:\> $a = 1
PS C:\> $b = 2
PS C:\> $a -eq $b
False

我已经创建了名为 $a 的变量并将其设置为包含值 1。在 Windows PowerShell 中,变量名总是以美元符号开头,因此容易辨认。从技术角度来说,= 是指赋值运算符,因为它用来赋值。接下来,我会创建第二个变量 $b 并将值 2 赋给它。然后进行实际的逻辑比较 - 我要求 Windows PowerShell 利用 -eq(相等)运算符来比较 $a 和 $b 的内容。PowerShell 执行比较,确定两个值不相等,并显示结果:False。

Windows PowerShell 运算符与您可能见过的其他脚本语言有点不同,甚至不同于 C#。大多数语言使用 = 运算符来检查是否相等以及执行赋值操作;而 Windows PowerShell 通过对每个函数采用专用的运算符来避免混淆。图 1 显示 Windows PowerShell 比较运算符,以及您可能已经熟悉的其他语言(如 VBScript)中的相等运算符。

Figure 1 PowerShell 比较运算符

运算符 名称 说明
-eq 相等 测试值是否相等。其他语言可能使用 = 或 == 来测试相等。
-ne 不等 测试不等。其他语言可能使用 <> 或 != 来测试不等。
-gt 大于 测试一个值是否大于另一个值。其他语言可能使用 > 字符。
-lt 小于 测试一个值是否小于另一个值。其他语言可能使用 < 字符。
-ge 大于或等于 测试一个值是否大于或等于另一个值。与 VBScript 和其他语言中的 >= 类似。
-le 小于或等于 测试一个值是否小于或等于另一个值。与 VBScript 和其他语言中的 <= 类似。

这些比较运算符有另一种有趣的功能。看看该比较:

PS C:\> $a = "TechNet"
PS C:\> $b = "technet"
PS C:\> $a -eq $b
True

默认情况下,比较运算符不区分大小写,意味着大写的 TechNet 将视为等价于“technet”。那很方便,因为在大多数管理任务中您并不关心字母的大小写问题。但是,有时候您可能会关心,而您可以要求 Windows PowerShell 将字母 c 置于比较运算符之前以执行区分大小写的比较:

PS C:\> $a -ceq $b
False

同样,如果您确实想知道或不确定 Windows PowerShell 是否会执行不区分大小写的比较,您可以通过预置字母 i 来强制它这样做:

PS C:\> $a -ieq $b
True

只要记住逻辑比较总是产生两种结果之一:True 或 False。

做决定

既然您知道了如何编写逻辑比较,您可以开始在构造中使用它们。我将向您展示的第一个构造允许 Windows PowerShell 在比较的基础上做出决定。它称为 If 构造,并且有几种变形形式。以下是最简单的形式:

PS C:\> if ($a -eq $b) {
>> Write-Host "They are equal"
>> }
>>
They are equal

此处有一些有趣的问题需要注意。首先,变量 $a 和 $b 仍然分别包含值 "TechNet" 和 "technet"。我使用 If 关键字来作为构造的开头。之后,在括号中我输入了要执行的逻辑比较。接着是花括号,表示我将调用的条件代码(如果比较时返回的结果为 True,则为 Windows PowerShell 将要执行的代码)的开始。您从之前的示例可以知道该比较确实返回 True,因此我期望执行条件代码。我键入我的条件代码 Write-Host "They are equal",然后按 Enter。最后,我键入右花括号结束条件代码段,并敲击 Enter 两次。(第二次是在空白行上敲击 Enter,这让分析器知道我已经完成了编写并准备好执行代码。)

注意该构造并不从脚本文件运行。我只需将它键入 Windows PowerShell 命令行。这使得 Windows PowerShell 在 Windows 的脚本世界里独一无二:脚本可以交互式地创建,也可以放入文件以做长久的存储。

我一键入左花括号并按 Enter,Windows PowerShell 就显示 >> 提示符。那是它的表达方式,意思是“我识别出您位于构造中,并且我已准备好让您在构造内键入任何内容。”在我键入右花括号并敲击 Enter 两次后,Windows PowerShell 立即执行构造,确定其逻辑比较结果是 True 并执行条件代码。您可以看见该内容,因为 "They are equal" 在 PowerShell 返回到其正常提示符前显示。使用 Windows PowerShell 来交互式地编写脚本允许您在将代码构建成为更长期的脚本前快速测试它们,使得学习和调试都更加容易。

我要指出 Windows PowerShell 对按 Enter 这类的事并不是要求特别高。例如,这在功能上与上一示例相同:

PS C:\> if ($a -eq $b) { Write-Host "They are equal" }
They are equal

因为这些内容我都在一行上键入,所以 Windows PowerShell 不需要显示特殊的 >> 提示符;它只需在我于行末按 Enter 时执行构造。Windows PowerShell 是如何知道可以执行构造了的呢?因为它在那个点处是完整的 - 已键入右花括号。

我提到 If 构造的其他变形形式。以下是一个完整的示例,以其在 PS1 脚本文件而不是命令解释程序中的形式显示:

if ($a -eq $b) {
  Write-Host "They are equal"
} elseif ($a -lt $b) {
  Write Host "One is less than the other"
} else {
  Write Host "One is greater than the other"
}

构造同样使用 If 关键字作为开头。但是,在比较结果为 False 的情况下,我使用 Elseif 关键字来提供了另一种比较。如果第二个比较结果也是 False,则最后一个关键字 Else 会提供将执行的最后一组代码。

重复自身

Windows PowerShell 包含几个用于反复执行代码的构造,直到比较结果为 True 或 False。程序员调用这些循环构造。更好的是,其中最有用的一个循环构造可以枚举集合中的对象并为每个对象执行一行或多行代码。更确切地说,构造称为 foreach 构造,形式如下:

PS C:\> $names = get-content "c:\computers.txt"
PS C:\> foreach ($name in $names) {
>> Write-Host $name
>> }
>>
don-pc
testbed

首先我要求 Windows PowerShell Get-Content cmdlet 检索 c:\computers.txt 文件,这是我自己创建的文件,每行包含一个计算机名称。Windows PowerShell 将每一行作为对象来处理,因此文件实质上是包含这些对象的集合。集合以变量 $names 结束。使用 Foreach 关键字,我告诉 Windows PowerShell 通过利用变量 $names 在每次循环执行时表示当前对象来枚举 $names 集合。循环代码在花括号中。因此,对于文件中的每个名称,我都将输出到命令行。并且,如同您可以从构造后的输出看到的一样,那正是 Windows PowerShell 所做的。您可以看到这是如何在管理脚本中带来明显的益处的:例如,您可以轻松地构造服务器名称列表,并让 Windows PowerShell 依次从每一个中检索信息。

实际构造

那么,让我们利用 If 构造和 foreach 构造来进行逻辑比较,并做一些有用的事情。我要快速检查一组服务器上的“Messenger”服务的状态。我期望在大多数服务器上服务将停止,因此我不想让 Windows PowerShell 列出所有其中的服务处于我所期望的状态的服务器;我只想让它列出 “Messenger” 服务实际已启动的服务器,因为我需要对这些服务器进行某些操作。

我知道 Windows PowerShell Get-Service cmdlet 可以有助于检索我需要用于本地计算机的信息。但遗憾的是,它无法访问远程计算机,那才是我真正的目的。令人高兴的是,我也可以使用 Get-WMIObject cmdlet,通过 Windows Management Instrumentation (WMI) 来访问相同的信息,这使我可以使用远程计算机。以下是脚本:

$names = get-content c:\computers.txt
foreach ($name in $names) {
  $svc = get-wmiobject win32_service '
   -computer $name -filter "name='messenger'"
  if ($svc.started -eq $True ) {
    write-host "$name has Messenger running"
  }
}

留意到第三行的 ' 字符了吗?它告诉 Windows PowerShell 下一行是延续部分。它在整行如果不换行就无法置于库中这种情况下有用。还要注意 If 构造将 $svc.started 与 $True 比较。变量 $True 是 Windows PowerShell 中的特殊变量,表示布尔 True 值。(同伴变量 $False,表示布尔 False。)实际上,在那里我本可以走一些捷径:

$names = get-content c:\computers.txt
foreach ($name in $names) {
  $svc = get-wmiobject win32_service '
   -computer $name -filter "name='messenger'"
  if ($svc.started) {
    write-host "$name has Messenger running"
  }
}

记住,If 构造的条件只需要为 True 或 False。通常,您通过比较两个值获得 True 或 False,就像我在该脚本的第一个版本中所做的那样。但是,由于“Started”属性为 True 或 False,因此无需将其与 True 或 False 比较。

一种有用的工具

至此您已经获得了一种利用构造来发挥作用,简单而有用的管理工具。无论您是交互式地将它键入 Windows PowerShell,还是在 PS1 文件中保存它以轻松地复用,它都是一种检查各种计算机上服务状态的方便的工具,并且很好地演示了构造是如何有助于自动执行管理任务的。

Don Jones 是 SAPIEN Technologies 的项目和服务主管,也是《Windows PowerShell: TFM》(SAPIEN Press,2006)的合著者。通过网站联系 Don,网址是 ScriptingAnswers.com

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