PowerShell 技能连载 - 展开对象数据结构

适用于 PowerShell 3.0 及以上版本

对象有可能包含嵌套的属性,您所关心的数据可能在一个对象中的“某个地方”。

要显示某个对象的所有属性和展开后的子属性,可将它转换为 JSON。这能很好地以文本的方式展现嵌套的属性。

这个例子获取当前的 PowerShell 进程,然后将它转化为 JSON,然后将文本输出到剪贴板。然后就可以将内容粘贴到任意的文本编辑器:

Get-Process -Id $pid  | ConvertTo-Json | clip.exe

要控制递归的深度,请使用 ConvertTo-Json 命令的 -Depth 参数。缺省值是 2(所以最多显示 2 层递归深度的内容)。

PowerShell 技能连载 - 禁止确认信息

适用于 PowerShell 所有版本

某些 cmdlet(例如 Remove-ADGroupMember)会自动提示确认信息。这在无人值守的脚本中会有问题。

要禁用这些不需要的确认对话框,请显式将 -Confirm 开关参数设置为 false

Remove-ADGroupMember -Identity 'Domain Admins' -Members user12 -Confirm:$false

或者,您可以修改安全级别。PowerShell 将会自动读取该设置。这行代码将所有 cmdlet 的自动确认关闭:

$ConfirmPreference = 'None'

请注意两个技能都只对缺省的确认对话框有效。如果一个 cmdlet 以 PowerShell 确认框架之外的方式提示确认,您需要参阅 cmdlet 的文档来查找如何禁止它的方法。

PowerShell 技能连载 - 随时获取 cmdlet 的帮助

适用于 PowerShell 3.0 及以上版本

从 PowerShell 3.0 开始,帮助文件不在随着 PowerShell 发布。相反,您需要通过 Update-Help 来下载它们,并且由于帮助文件是存储在(受保护的)PowerShell 文件夹中,所以一个普通用户无法实现该操作。

将来您需要某个 cmdlet 的帮助时,只需要直接使用在线版即可。以下代码将通过浏览器访问 Get-Process 的在线帮助(假设您有 Internet 连接):

PS> help Get-Process -Online

安装了帮助文件之后,在 PowerShell ISE 中获得帮助变得更容易了:只需要点击任何 cmdlet 名称,然后按 F1 键。

如果仔细观察,会发现当按下 F1 时,实际上是帮您输入了帮助命令。所以如果需要同样的操作方便性,但是显示的是在线的帮助,您以实现一个类似这样的函数:

function Get-Help($Name) { Get-Help $Name -Online }

然而,这可能会导致死循环,因为新创建的 Get-Help 会在内部调用自己。要让它正常工作,您需要您的函数内部是采用类似这样的方式调用原始的 Get-Help cmdlet:

function Get-Help($Name) { Microsoft.PowerShell.Core\Get-Help $Name -Online }

当您运行这个函数是,您可以点击 PowerShell ISE 中的任意 cmdlet 名称,然后按 F1 键,就能够访问该 cmdlet 的在线解释——忽略您本机安装的帮助文件。

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"
}