PowerShell 技能连载 - 播放 WAV 文件

适用于 PowerShell 所有版本

以下是一个简单的用 PowerShell 播放 WAV 声音文件的方法:

# find first available WAV file in Windows folder
$WAVPath = Get-ChildItem -Path $env:windir -Filter *.wav -Recurse -ErrorAction SilentlyContinue |
  Select-Object -First 1 -ExpandProperty FullName


# load file and play it
"Playing $WAVPath..."

$player = New-Object Media.SoundPlayer $WAVPath
$player.Play()

"Done!"

这段脚本的第一部分在 Windows 文件夹中查找第一个 WAV 文件。当然,您可以将您喜欢的 WAV 文件路径赋给 $WAVFile

下一步, Media.SoundPlayer 读取并播放 WAV 文件。请注意 Play() 如何播放声音:它在一个单独的线程中播放,而 PowerShell 将会立即继续运行。

您可以用这种方法来创建一个有声的进度条:当 PowerShell 在做某件事的时候持续播放声音:

# find first available WAV file in Windows folder
$WAVPath = Get-ChildItem -Path $env:windir -Filter *.wav -Recurse -ErrorAction SilentlyContinue |
  Select-Object -First 1 -ExpandProperty FullName


# load file and play it

$player = New-Object Media.SoundPlayer $WAVPath
$player.PlayLooping()

1..100 | ForEach-Object {
  Write-Progress -Activity 'Doing Something. Hang in' -Status $_ -PercentComplete $_
  Start-Sleep -MilliSeconds (Get-Random -Minimum 300 -Maximum 1300)
  }
$player.Stop()

现在,PlayLooping() 用于循环播放声音。该声音会一直播放下去,所以您需要手动调用 Stop()。这是脚本结束的时候需要做的事情。

PowerShell 技能连载 - PowerShell ISE 自动化

适用于 PowerShell 3.0 及以上版本

PowerShell ISE 是可以通过 $psISE 脚本化编程的。这个变量只在 PowerShell ISE 环境中有效。

要获得当前可见的脚本内容,请试试以下代码:

PS> $psise.CurrentFile.Editor.Text

这段代码可以实现一个简单的重命名方法,将 PowerShell ISE 当前打开的脚本中的 所有 “testserver” 替换成 “productionserver”(前提是在您当前的脚本代码中有一系列 “testserver” 字眼出现):

$psise.CurrentFile.Editor.Text = $psise.CurrentFile.Editor.Text -replace 'testserver', 'ProductionServer'

PowerShell 技能连载 - 查找进程所有者

适用于 PowerShell 3.0 及以上版本

Get-Process 能够返回所有运行中的进程,但是它并不包含进程所有者。要查看进程所有者,您需要采用调用 WMI 服务等方法。

要让使用过程更方便些,一下是一个小巧的工具函数:

filter Get-ProcessOwner
{
  $id = $_.ID
  $info = (Get-WmiObject -Class Win32_Process -Filter "Handle=$id").GetOwner()
  if ($info.ReturnValue -eq 2)
  {
    $owner = '[Access Denied]'
  }
  else
  {
    $owner = '{0}\{1}' -f $info.Domain, $info.User
  }
  $_ | Add-Member -MemberType NoteProperty -Name Owner -Value $owner -PassThru
}

当您将进程对象通过管道传递给 Get-ProcessOwner 时,它为进程对象附加了一个新的 “Owner“ 属性。该属性是一个隐藏属性,要通过 Select-Object 才能显示:

PS> Get-Process -Id $pid | Get-ProcessOwner | Select-Object -Property Name, ID, Owner

Name                    Id Owner
----                    -- -----
powershell_ise       10080 TOBI2\Tobias

它也适用于多个进程对象:

PS> Get-Process | Where-Object MainWindowTitle | Get-ProcessOwner | Select-Object -Property Name, ID, Owner

Name                    Id Owner
----                    -- -----
chrome               13028 TOBI2\Tobias
devenv               13724 TOBI2\Tobias
Energy Manager        6120 TOBI2\Tobias
ILSpy                14928 TOBI2\Tobias
(...)

请注意只有获得了管理员权限才可以获取进程所有者。如果没有货的管理员权限,您只能获得您自己进程的所有者,这样相对意义不大。

PowerShell 技能连载 - 使用基于 JSON 的 Web Service

适用于 PowerShell 3.0 及以上版本

Internet 有许多信息提供服务,许多返回的是 JSON 数据格式。以下是一个演示如何用 PowerShell 查询这类 Web Service,并将 JSON 结果转换为对象的例子。

这个例子使用一个德国铁路公司的 Web Service。您只要输入火车站或城市名字的一部分,就能得到包含输入信息的所有火车站名:

# ask for part of the train station name
$name = Read-Host 'Enter part of train station Name'

# query webservice
$url = "http://openbahnapi.appspot.com/rest/stations/list?contains=$name"
$site = Invoke-WebRequest -Uri $url

# get JSON result
($site.Content | ConvertFrom-Json ).value

结果看起来类似这样:

PS> Enter part of train station name: hanno
Hannover Hbf
HANNOVER MESSE
Hannoversch Münden
Hannover-Nordstadt
Hannover Bismarckstr.
Hannover Karl-Wiechert-Allee
Hannover-Ledeburg
Hannover-Linden/Fischerhof
Hannover-Vinnhorst
Hannover-Leinhausen
Hannover Anderten-Misburg
Hannover-Bornum

PS>

这段代码并不仅限于德国铁路系统,所以如果您不是对德国铁路系统特别兴趣的话,可以调用任何基于 JSON 的 Web Service。

重要的环节是 Invoke-WebRequest(该命令和 Web Service 通信并返回结果),以及 ConvertFrom-Json(该命令接收上一步的结果并将它转换为对象)。

请注意 Web Service 的提供者可能随时改变服务的实现。示例代码中的 Web Service 只能当做学习的例子来用。

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“ 后的文本将变为折叠区域的标题。