PowerShell 技能连载 - 用 Select-Object -First 节省时间!

Select-Object 命令有一个 -First 参数,接受一个数字值。它将只返回前 x 个元素。听起来挺简单的,而且它的确就这么简单。

以下代码从您的 Windows 文件夹中获取前 4 个 PowerShell 脚本:

从 PowerShell 3.0 开始,-First 不仅选择指定数量的结果,而且它还通知管道的上游命令,告知它操作已完成,有效地中止管道操作。

所以如果您想得到某个命令一定数量的结果,那么您总是可以使用 Select-Object -First x ——在特定的场景里这可以显著地加速您的代码执行效率。

我们假设您需要在您的用户文件夹之下的某个地方找一个名为“test.txt”的文件,并且假设只有一个这个名字的文件。而您只是不知道它放在哪个位置,那么您可以使用 Get-ChildItem-Recurse 来递归查找所有的文件夹:

Get-ChildItem -Path $home -Filter test.txt -Recurse -ErrorAction SilentlyContinue

当您执行这段代码时,Get-ChildItem 最终将找到您的文件——并且继续搜索您的文件夹树,也许要持续几分钟才能找完。因为它不知道是否有其它的文件。

所以,您知道的,如果您事先确定结果的数量,那么试试以下的代码:

Get-ChildItem -Path $home -Filter test.txt -Recurse -ErrorAction SilentlyContinue |
  Select-Object -First 1

这一次,Get-ChildItem 将会在找到一个文件后立即停止。

PowerShell 技能连载 - 展开字符串中的变量

要在一个字符串中插入一个变量,您也许已经知道可以使用如下的双引号方式:

$domain = $env:USERDOMAIN
$username = $env:USERNAME

"$domain\$username"

对于 PowerShell 来说这些变量的起止范围是没有歧义的。所以它可以工作正常。然而试试以下代码:

$domain = $env:USERDOMAIN
$username = $env:USERNAME

"$username: located in domain $domain"

这段代码执行失败了,这是因为 PowerShell 在变量中添加了冒号(从语法彩色中也可以看出)。

您可以采用 PowerShell 的反引号来为特殊字符(比如说冒号)转义:

$domain = $env:USERDOMAIN
$username = $env:USERNAME

"$username`: located in domain $domain"

如果问题不是由于特殊字符引起的,那么这种方法没有作用:

"Current Background Color: $host.UI.RawUI.BackgroundColor"

语法高亮提示双引号引起来的字符串中只解析出了变量,而其它部分(变量名之后的部分,比如说对象的属性)并没有解析出来。

要解决这个问题,您需要使用以下的方法之一:

"Current Background Color: $($host.UI.RawUI.BackgroundColor)"
'Current Background Color: ' + $host.UI.RawUI.BackgroundColor
'Current Background Color: {0}' -f $host.UI.RawUI.BackgroundColor

PowerShell 技能连载 - 使用别名来启动 Windows 组件

PowerShell 不仅是一个自动化操作的语言,而且是另一个用户操作界面。如果您不喜欢图形界面,那么练习使用 PowerShell 通过简单的别名来启动您需要的工具。

例如,要打开设备管理器,您可以使用它的原始名称:

如果您不想记忆这个名称,那么使用别名:

如您所见,要打开设备管理器,您现在所要做的只是键入“DeviceManager”。您也可以只键入“Device”然后按下 TAB 键来使用自动完成功能。

当关闭 PowerShell 时,定义的别名将会消失。所以要保持您定义的别名有效,请将 Set-Alias 命令加入您的配置脚本。配置脚本的路径可以在 $profile 中找到。如果这个目录不存在,您可能需要事先创建这个文件(以及它所在的文件夹)。Test-Path 可以检测它是否已经存在。

PowerShell 技能连载 - 过滤命令输出的文本

比较操作符作用于数组时,它们的作用和过滤器相似。所以许多输出多行文本的控制台命令可以使用比较操作符。

以下例子将使用 netstat.exe 来获取已连接上的网络连接,然后过滤出连到名字包含“stor”的服务器的连接,最后用 ipconfig 来获取当前的 IPv4 地址:

这个技巧是将控制台命令用 @() 括起来,确保结果为一个数组。

PowerShell 技能连载 - 持有一个进程的句柄

当您打开一个 EXE 文件时,PowerShell 将会开心地启动它,然后什么也不管:

如果您希望持有该进程的句柄,比如希望获得它的进程 ID,或者过一会儿检查该进程运行得如何,或者要中止它,请使用 Start-Process–PassThru 参数。以下代码将返回一个进程对象:

PowerShell 技能连载 - 使用 $PSScriptRoot 加载资源

从 PowerShell 3.0 开始,有一个称为 $PSScriptRoot 的变量可用。该变量之前只在模块中可用。它总是指向当前脚本所在的文件夹(所以它仅在您明确地保存了它以后才生效)。

您可以使用 $PSScriptRoot 来加载相对于您脚本位置的额外资源。例如,如果您打算将一些函数放在同一个文件夹中的一个独立的“library”脚本中,以下代码将加载该 library 脚本并且导入它包含的所有函数:

# this loads the script "library1.ps1" if it is located in the very
# same folder as this script.
# Requires PowerShell 3.0 or better.

. "$PSScriptRoot\library1.ps1"

类似地,如果您希望将您的 library 脚本保存在一个子文件夹中,请试验以下脚本(假设库脚本放在您脚本所在文件夹中的“resources”子文件夹下):

# this loads the script "library1.ps1" if it is located in the subfolder
# "resources" in the folder this script is in.
# Requires PowerShell 3.0 or better.

. "$PSScriptRoot\resources\library1.ps1"

PowerShell 技能连载 - 用 PowerShell 为 VBScript 文件签名

您很可能知道 Set-AuthenticodeSignature 可以用来为 PowerShell 脚本签名。但您是否知道这个 cmdlet 可以为任何支持目标接口包 (SIP) 的任何文件呢?

这段代码可以从一个 PFX 文件中读取数字证书,然后从您的 home 文件夹中扫描 VBScript 文件,然后将数字签名应用到脚本文件上:

# change path to point to your PFX file:
$pfxpath = 'C:\Users\Tobias\Documents\PowerShell\testcert.pfx'
# change password to the password needed to read the PFX file:
# (this password was set when you exported the certificate to a PFX file)
$password = 'topsecret'

# load certificate
Add-Type -AssemblyName System.Security
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import($pfxpath, $password, 'Exportable')

# apply signature to all VBScript files
# REMOVE -WHATIF TO ACTUALLY SIGN
Get-ChildItem -Path $home -Filter *.vbs -Recurse -ErrorAction SilentlyContinue |
  Set-AuthenticodeSignature -Certificate $cert -WhatIf

PowerShell 技能连载 - 正确地按日期时间筛选

当您使用 Where-Object 通过日期或时间来过滤信息时,它工作得很好——前提是您使用了正确的过滤格式。请不要使用输出结果中的格式。

要指定一个日期或时间,请永远使用中性文化的格式:

“year-month-day hour:minute:second”,所以 2014 年 5 月 14 日应该表述成这样:“2014-05-12 12:30:00”。

或者换种方法处理:当您输出结果时,PowerShell 将根据您控制面板的设置来格式化日期和时间。当您输入信息(例如过滤规则)时,PowerShell 永远期望接收一个通用的日期和时间格式。这是有道理的:脚本须在任何文化环境中运行一致。而结果需要格式化成读者的语言文化格式。

所以要在您的 Windows 文件夹中查找所有自从 2012 年 4 月 30 日以来没有修改过的文件,请尝试以下代码:

PowerShell 技能连载 - 在 PowerShell ISE 中创建干净的测试环境

当您在 PowerShell ISE 编辑器中开发 PowerShell 脚本时,您需要在一个干净的环境中运行最终的测试,确保不会被之前运行时残留的变量或者函数干扰。

创建一个干净的测试环境的最简单方法是:选择文件菜单,然后选择“新建 PowerShell 选项卡”。这将为您带来一个新的标签页,并且该页代表一个全新的 PowerShell 宿主。完美的测试环境!

PowerShell 技能连载 - 从 PFX 文件中导入多个证书

Get-PfxCertificate 可以从 PFX 文件中导入数字证书。然而,他只能获取一个证书。所以如果您的 PFX 文件中包含多个证书,您无法使用这个 cmdlet 获取其它的证书。

若要从一个 PFX 文件中导入多个证书,只要使用以下代码:

$pfxpath = 'C:\PathToPfxFile\testcert.pfx'
$password = 'topsecret'

Add-Type -AssemblyName System.Security
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
$cert.Import($pfxpath, $password, 'Exportable')
$cert