PowerShell 技能连载 - 使用数组作为参数的缺省值

适用于 PowerShell 3.0 及以上版本

如果您定义的 PowerShell 函数有几个参数,并且希望某个参数的缺省值是一个数组,您可能会遇到语法问题:

function Get-SomeData
{
  param
  (
    $ServerID = 1,2,5,10,11
  )

  "Your choice: $ServerID"
}

PowerShell 使用逗号来分隔参数,所以在 param() 块中的 “1” 之后的逗号会被曲解,PowerShell 会认为这是一个接下来定义的新参数。

当会发生歧义的时候,使用圆括号来确保这些部分是一个整体。以下是一段完美合法的代码:

function Get-SomeData
{
  param
  (
    $ServerID = (1,2,5,10,11)
  )

  "Your choice: $ServerID"
}

PowerShell 技能连载 - 简化命令提示符

适用于 PowerShell 3.0 及以上版本

缺省情况下,PowerShell 的命令提示符中包含了当前路径。当您以一个普通用户启动 PowerShell 时,当前路径就是您的用户路径。那是一个很长的路径,会占用命令行很多空间。

最有效最简单的方法是将当前目录改为根目录:

PS C:\Users\Tobias\Documents> cd \

PS C:\>

或者,可以调整 prompt 函数,使它在其它地方显示当前路径,例如在标题栏:

function prompt
{
  'PS> '
  $host.UI.RawUI.WindowTitle = Get-Location
}

PowerShell 技能连载 - 获取 DELL 保修信息(第二部分)

适用于 PowerShell 2.0 及以上版本

在前一个技巧中我们演示了如何用一个 Web Service 来获取 DELL 电脑的保修信息。我们收到了许多反馈,所以在我们介绍新内容之前,先展示这段可以获取保修信息的代码:

$serial = '36GPL41'

$service = New-WebServiceProxy -Uri http://143.166.84.118/services/assetservice.asmx?WSDL
$guid = [Guid]::NewGuid()

$info = $service.GetAssetInformation($guid,'warrantycheck',$serial)
$Entitlements = $info.Entitlements

$Entitlements

现在,如果您试着将 $Entitlements 加到一个用户界面的文本框中,或者将它输出为文本,结果可能不是您想要的:

PS> "Your Entitlements: $Entitlements"
Your Entitlements: Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy1ervices_assetservice_asmx_WSDL.EntitlementData Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy1ervices_assetservice_asmx_WSDL.EntitlementData Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy1ervices_assetservice_asmx_WSDL.EntitlementData

PS>

这是因为 $Entitlements 是一个对象数组。当将它以文本方式显示时,我们希望 PowerShell 扩展类型系统能处理这些对象,所以将它们用 Out-String 处理一下:

PS> $EntitlementsText = $Entitlements | Out-String

PS> "Your Entitlements: $EntitlementsText"
Your Entitlements:

ServiceLevelCode        : TS
ServiceLevelDescription : P, ProSupport
Provider                : DELL
StartDate               : 23.03.2004 00:00:00
EndDate                 : 23.03.2007 00:00:00
DaysLeft                : 0
EntitlementType         : Expired

ServiceLevelCode        : ND
ServiceLevelDescription : C, NBD ONSITE
Provider                : UNY
StartDate               : 23.03.2005 00:00:00
EndDate                 : 23.03.2007 00:00:00
DaysLeft                : 0
EntitlementType         : Expired

ServiceLevelCode        : ND
ServiceLevelDescription : C, NBD ONSITE
Provider                : UNY
StartDate               : 23.03.2004 00:00:00
EndDate                 : 24.03.2005 00:00:00
DaysLeft                : 0
EntitlementType         : Expired

PowerShell 技能连载 - 将文件的扩展名正常化

适用于 PowerShell 2.0 及以上版本

假设您希望用户提交一个文件扩展名的列表,或者是从某些其它来源获取这个列表。

文件扩展名是模糊标准的绝好例子。您要如何指定一个文本文件的扩展名呢?是用 “.txt” 还是 “*.txt”?

以下是一个将文件的扩展名正常化的简单技巧,无论它们如何拼写都有效:

$extensions = '*.ps1', '.txt'
$cleanExtensions = $extensions -replace '^\.', '*.'

$extensions
$cleanExtensions

PowerShell 技能连载 - 使用 PowerShell ISE 调试器

适用于 PowerShell 3.0 及以上版本

有些时候,很难找出 PowerShell 脚本为什么不按期望工作的原因。要更好地了解脚本工作的过程,请使用 PowerShell ISE 内置的调试器。

在您开始调试一个脚本之前请先保存它。无标题的脚本实际上还不算脚本,所以 PowerShell ISE 还无法调试它。

以下是调试一个脚本的简单步骤:

  1. 设置断点。断点是您希望调试器在代码中停下的位置,这样您可以检查当前变量的状态。要设置一个断点,请单击某一行,然后按下 F9 键。该行代码将会变成红色。如果该行未变红,说明您还没有保存该脚本,或是该行代码不包含可执行的代码。

  2. 运行脚本:脚本将会正常运行,但当运行到一个断点时,PowerShell ISE 将会暂停。当前行会标记成黄色。您可以将光标悬停到代码中的变量上来查看它们的值,或是在交互式窗口中执行任意代码,例如导出变量的值,甚至改变变量的值。

  3. 继续:要继续执行下一条指令,请按 F10F11F10 将执行当前作用域内的下一条指令。F11 将执行下一条指令,无论是哪个作用域。所以如果当前行将要执行一个函数,而您按下 F10,那么将执行整个函数。如果您按下 F11,那么您将执行到该函数的第一行。这有点像“一小步”的概念。

  4. 按下 F5 继续执行整个脚本。它将运行到整个脚本结束,或是遇到下一个断点。

  5. 按下 SHIFT+F5 退出执行过程并且停止调试器。

一旦您掌握了这些步骤,调试过程会变得十分容易。并且它可以给您带来许多领悟和帮助。如果不采用调试方法的话将很难调查脚本错误。

PowerShell 技能连载 - 在 ISE 中使用代码区域

适用于 PowerShell 3.0 及以上版本

PowerShell ISE 已经支持了可折叠的代码区域。当您编写函数、循环,或是条件时,您也许会注意到在左边距的上方有一个“减号”符号。点击它可实现折叠该区域。

如果看不到区域特性,您可以这样启用它们:视图/显示大纲(区域)。

您也可以在脚本的其它部分使用区域和代码折叠。要将代码的任意部分包括在一个可折叠的区域内,请在代码中加入一些特殊的注释:

#region Variable Declarations
$a = $b = $c = 1
$d, $e, $f = 2,3,4
#endregion

请注意这些特殊的注释是大小写敏感的。在将区域折叠之后,”#region“ 后的文本将变为折叠区域的标题。

PowerShell 技能连载 - 进程终结器(和一些陷阱)

适用于 PowerShell 3.0 及以上版本

在前一个技能中我们介绍了如何利用 Out-GridView 做一个选择对话框,并且提供了一些建议。一个点子是列出所有桌面应用,并且允许用户选择一个进程并终结它。

要列出所有桌面应用,请试试这段代码:

PS> Get-Process | Where-Object MainWindowTitle | Select-Object -Property Name, Description, MainWindowTitle, StartTime

这行代码对进程列表进行过滤并只列出设置了 MainWindowTitle 的进程。实际上,它返回了一个包含窗体的列表,忽略了所有不可见的后台进程。

接下来,将结果通过管道输出到 Out-GridView 并允许单选:

PS> Get-Process | Where-Object MainWindowTitle | Select-Object -Property Name, Description, MainWindowTitle, StartTime | Out-GridView -Title 'Kill Application' -OutputMode Single | Stop-Process -WhatIf

这行代码将打开一个网格视图窗口,显示所有运行中的进程。当您选中一个进程并点击“确定”按钮,将会杀掉该进程。不过,还差一点:这段示例代码包含了 -WhatIf 开关,所以只是 Stop-Process 只是模拟操作。

所以这是个好东西,因为您可能会注意到选择一个进程将会导致杀掉所有同名的进程。

这是由于 Stop-Process 可以接受两个不同的信息:名字(字符串),或是进程 ID (int)。由于这行代码使用了 Select-Object 来筛选属性,并且不包含进程 ID,所以 Stop-Process 将会使用进程名字,并杀掉所有同名的进程。

要实现杀除更具体的进程,请确保包含了进程的 ID:

PS> Get-Process | Where-Object MainWindowTitle | Select-Object -Property Name, Id, Description, MainWindowTitle, StartTime | Out-GridView -Title 'Kill Application' -OutputMode Single | Stop-Process -WhatIf

PowerShell 技能连载 - 将结果复制到剪贴板

适用于 PowerShell 3.0 及以上版本

在前一个技能中我们介绍了如何简单地从 Out-GridView 的网格视图窗口中复制粘贴信息。不过这并不会复制列头。

您可以将这行代码加到任意命令中,并将它的结果复制到剪贴板中(包括列头):

PS> Get-Service | Format-Table -AutoSize -Wrap | Out-String -Width 200 | clip.exe

当您运行完这行代码后,所有服务的清单就保存到剪贴板中了,接下来可以将内容粘贴到 Word 或其它接受文本输入的应用程序中。

请注意 Format-TableOut-String 的用法:它们确保数据不会按照 PowerShell 控制台的边界来格式化。相反地,可用的宽度被设为设为 200 字符,如果结果仍比这个长,那么将会折行。

如果忽略掉这两个 cmdlet,然后查看一下结果:如果没有它们,文本将会输出到 PowerShell 控制台。过长的结果将会被截断。

为了简化操作,您可以将这行代码封装为一个简单的函数,例如:

PS> function Out-Clipboard { $input | Format-Table -AutoSize -Wrap | Out-String -Width 1000 | clip.exe }

现在,当您想将结果复制到剪贴板时,可以使用 Out-Clipboard

PS> Get-Process | Out-Clipboard

PowerShell 技能连载 - Out-GridView:通用对话框

适用于 PowerShell 3.0 及以上版本

默认情况下,Out-GridView 是一条单行道:您可以将数据用管道输出到该命令,将结果显示在一个网格视图窗口中,但是您无法将数据再往下传递。

当您添加了 -PassThru 开关参数时,情况就变了。这时 Out-GridView 的右下角显示了两个新按钮:“确定”和“取消”。它将自己变为一个通用的对话框。

试试这行代码:

PS> Get-Service | Where-Object CanStop | Out-GridView -Title 'Stoppable Services' -PassThru

这将打开一个标题为 “Stoppable Services” 的网格视图窗口,并列出所有可停止的服务(您可能还需要管理员权限才可以停止它们)。

您现在可以选择一个或多个项目(按住 CTRL 键多选),然后点击网格视图窗口右下角的“确定”按钮。

如您所见,返回了选中的对象。

要将这行代码变为一个有用的工具,您可以将 Out-GridView 的结果输出到 cmdlet,来执行具体的操作。这行代码将试图停止所有选中的服务:

PS> Get-Service | Where-Object CanStop | Out-GridView -Title 'Stoppable Services' -PassThru | Stop-Service -WhatIf

请注意,出于安全考虑,我们对 Stop-Service 命令增加了 -WhatIf 参数,所以该 cmdlet 只会模拟停止服务。当您移除了这个参数,该行代码就不是模拟执行,而是真实停止服务了。

只需要坐下喝杯咖啡,然后思考一下它的原理:Out-GridView 接受任何类型的数据,所以您可以用它创建任何工具。例如,使用 Active Directory cmdlet Get-ADUser 来查找当前禁用的用户,然后让 PowerShell 为您启用所有选中的用户。

或者显示一个有主窗口的进程(桌面应用),并且杀掉所有选中的进程。

如果想达到这个目的,您可能会期望 Out-GridView 禁止多选。要想只允许选择单条记录,请试试以下代码:

PS> 1..10 | Out-GridView -Title 'Pick favorite number' -OutputMode Single

PowerShell 技能连载 - 查找所有可停止的服务

适用于 PowerShell 3.0 及以上版本

Get-Service 可以列出您计算机上所有已安装的服务。不过它没有可以选择仅包含运行或停止的服务的参数。

用一个简单的 Where-Object 从句,您可以实现这个目的。最常见的是,您会见到类似如下的用法:

PS> Get-Service | Where-Object Status -eq Running

基本上,Where-Object 可以指定对象拥有的任意属性并且允许您定义需要的条件。

如果您打算获得一个可停止的服务的列表,那么上述代码不能达到您所要的目的。一些服务可能正在运行但是不能被停止。稍微调整一下过滤条件,您就可以达到所要的目的了。这段代码列出所有运行中并且可以停止的服务:

PS> Get-Service | Where-Object CanStop

并且这种写法缩短了代码量:由于 “CanStop“ 属性本身是个布尔值(truefalse),所以无需使用比较运算符。

要查看这个列表的补集,即所有不可停止的服务,可使用比较运算符:

PS> Get-Service | Where-Object CanStop -eq $false

请注意用 Where-Object 的简化语法,您无法取得相反的结果。以下代码并不会生效:

PS> Get-Service | Where-Object !CanStop

PS> Get-Service | Where-Object -not CanStop

要使用这些条件,或者要合并比较条件,请使用完整语法:

PS> Get-Service | Where-Object { !$_.CanStop -and $_.Status -eq 'Running' }