PowerShell 技能连载 - 检测 Wi-Fi 适配器和电源

适用于 Windows 8.1/Server 2012 R2

Windows 8.1 和 Server 2012 R2 带来了一系列好用的网卡管理 cmdlet。例如当您希望调查 Wi-Fi 连接问题时,或是检查为什么 Wake-On-LAN 功能未能唤醒机器时,检查网卡电源管理设置就十分有意思了。

现在是小菜一碟了:

PS> Get-NetAdapter

Name                      InterfaceDescription                   ifIndex Status

----                      --------------------                    ------- -----
Bluetooth-Netzwerkverb... Bluetooth-Gerät (PAN)                         7 Di...
WiFi                      Intel(R) Wireless-N 7260                      3 Up



PS> Get-NetAdapter -Name WiFi

Name                      InterfaceDescription                   ifIndex Status

----                      --------------------                    ------- -----
WiFi                      Intel(R) Wireless-N 7260                      3 Up

只要您知道网卡的名称,那么查询它电源管理设置的方法是:

PS> Get-NetAdapter -Name WiFi | Get-NetAdapterPowerManagement


InterfaceDescription    : Intel(R) Wireless-N 7260
Name                    : WiFi
ArpOffload              : Enabled
NSOffload               : Enabled
RsnRekeyOffload         : Enabled
D0PacketCoalescing      : Enabled
SelectiveSuspend        : Unsupported
DeviceSleepOnDisconnect : Disabled
WakeOnMagicPacket       : Enabled
WakeOnPattern           : Enabled

请注意您需要管理员权限来查看电源管理设置,否则会得到一个误导性的错误信息,提示设备工作不正常。

PowerShell 技能连载 - 用 Finally 来处理关键的清理任务

适用于 PowerShell 2.0 及以上版本

在前一个技能中我们介绍了一个“有声的进度条”,它能令 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()

这个脚本工作是正常的——除非您把它中断,例如按下 CTRL+C。如果按下这个组合键,脚本立即停止执行,并且 $player.Stop() 没有机会执行所以没有机会停止声音。

这就使用 PowerShell 中的 finally() 最为合适。它确保一个脚本退出前执行清理代码:

# 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

try
{

  $player.PlayLooping()

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

finally
{
  $player.Stop()
}

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