PowerShell 技能连载 - 在 PowerShell ISE 中创建干净的测试环境
当您在 PowerShell ISE 编辑器中开发 PowerShell 脚本时,您需要在一个干净的环境中运行最终的测试,确保不会被之前运行时残留的变量或者函数干扰。
创建一个干净的测试环境的最简单方法是:选择文件菜单,然后选择“新建 PowerShell 选项卡”。这将为您带来一个新的标签页,并且该页代表一个全新的 PowerShell 宿主。完美的测试环境!
当您在 PowerShell ISE 编辑器中开发 PowerShell 脚本时,您需要在一个干净的环境中运行最终的测试,确保不会被之前运行时残留的变量或者函数干扰。
创建一个干净的测试环境的最简单方法是:选择文件菜单,然后选择“新建 PowerShell 选项卡”。这将为您带来一个新的标签页,并且该页代表一个全新的 PowerShell 宿主。完美的测试环境!
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
当您尝试使用 PowerShell Remoting 时,您也许会因为您使用的机器名不是完整限定名而导致连接错误。Kerberos 验证可能需要也可能不需要使用完整限定名,这取决于您的 DNS 配置。
所以也许您使用如下方式连接的时候会发生错误:
Enter-PSSession -ComputerName storage1
当发生错误的时候,请向 DNS 查询完整限定名:
[System.Net.Dns]::GetHostByName('storage1').HostName
然后,用查出的名字来代替主机名。如果主机启用了 Remoting 并且正确地配置了,您现在应该可以连上了。
假设您创建了这个函数:
function Test-Function
{
param
(
[Parameter(Mandatory=$true)]
$ServerPath
)
"You selected $ServerPath"
}
它现在可以正常工作,但是在半年之后的代码审查中,您的老板希望您使用标准的参数名称,将“ServerPath”改名为“ComputerName”。那么您对您的代码做出适当的修改:
function Test-Function
{
param
(
[Parameter(Mandatory=$true)]
$ComputerName
)
"You selected $ComputerName"
}
然而,您不能很容易地控制哪些人调用了您的函数,而且他们使用了旧的参数。所以要确保向后兼容,请确保您的函数使用旧的参数名也可以工作:
function Test-Function
{
param
(
[Parameter(Mandatory=$true)]
[Alias("ServerPath")]
$ComputerName
)
"You selected $ComputerName"
}
旧的代码任然可以运行,并且新的代码(以及代码自动完成)将会使用新的名称:
您可以使用 Get-PfxCertificate
来从 PFX 文件中读取数字证书,然后用数字证书来为脚本文件签名,例如:
$pfxpath = 'C:\PathToPfxFile\testcert.pfx'
$cert = Get-PfxCertificate -FilePath $pfxpath
$cert
Get-ChildItem -Path c:\myscripts -Filter *.ps1 | Set-AuthenticodeSignature -Certificate $cert
然而,Get-PfxCertificate
将会交互式地询问您导出证书至 PFX 文件时所用的密码:
要静默地导入证书,请使用这段代码:
$pfxpath = 'C:\PathToPfxFile\testcert.pfx'
$password = 'topsecret'
Add-Type -AssemblyName System.Security
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import($pfxpath, $password, 'Exportable')
$cert
当您读取一个“可扩充字符串”类型的注册表值时,它将自动展开文本中的所有环境变量值。
这个例子将从注册表中读取系统设备路径:
$path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion'
$key = Get-ItemProperty -Path $path
$key.DevicePath
该结果将是实际的路径。这问题不大,除非您希望获取原始(未展开的)注册表值。以下是读取原始值的例子:
$path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion'
$key = Get-Item -Path $path
$key.GetValue('DevicePath', '', 'DoNotExpandEnvironmentNames')
通过这种方式存取注册表值可以提供额外的信息:您还可以获取该值的数据类型:
$path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion'
$key = Get-Item -Path $path
$key.GetValueKind('DevicePath')
PowerShell 可以很容易地读取环境变量。以下代码返回当前的 Windows 文件夹:
$env:windir
然而,如果您想永久地改变用户或机器的环境变量,您需要使用 .NET 的功能。以下是一个可以快速设置或删除环境变量的简单函数:
function Set-EnvironmentVariable
{
param
(
[Parameter(Mandatory=$true, HelpMessage='Help note')]
$Name,
[System.EnvironmentVariableTarget]
$Target,
$Value = $null
)
[System.Environment]::SetEnvironmentVariable($Name, $Value, $Target )
}
要创建一个永久的环境变量,试试以下代码:
PS> Set-EnvironmentVariable -Name TestVar -Value 123 -Target User
请注意新的用户变量只对新运行的应用程序可见。已运行的应用程序将会保持它们的运行环境副本,除非它们显式地请求改变后的变量。
以下是删除该环境变量的代码:
PS> Set-EnvironmentVariable -Name TestVar -Value '' -Target User
通常地,当您将一个函数参数标记为“必选的”,如果用户遗漏了这个参数,PowerShell 将提示用户:
function Get-Something
{
param
(
[Parameter(Mandatory=$true)]
$Path
)
"You entered $Path"
}
结果如下所示:
PS> Get-Something
cmdlet Get-Something at command pipeline position 1
Supply values for the following parameters:
Path:
以下是另一种选择:如果用户遗漏了 -Path
,该函数弹出一个打开文件对话框:
function Get-Something
{
param
(
$Path = $(
Add-Type -AssemblyName System.Windows.Forms
$dlg = New-Object -TypeName System.Windows.Forms.OpenFileDialog
if ($dlg.ShowDialog() -eq 'OK') { $dlg.FileName } else { throw 'No Path submitted'}
)
)
"You entered $Path"
}
也许您还没有意识到,PowerShell 在输入输出时用的是不同的十进制分隔符——这也许会导致脚本用户产生混淆。
当您输入信息时,PowerShell 接受的是语言中性的格式(使用“.”作为十进制分隔符)。当输出信息时,它使用的是您的区域设置(所以在许多国家,使用的是“,”)。
请实践一下看看以下是否和您的文化相符:
$a = 1.5
$a
1,5
这是一个良好的设计,因为使用语言中性的输入格式,脚本执行情况永远相同,无论区域设置如何。然而,如果您希望用户能使用逗号作为分隔符,请看以下脚本:
function Multiply-LocalNumber
{
param
(
[Parameter(Mandatory=$true)]
$Number1,
$Number2 = 10
)
[Double]$Number1 = ($Number1 -join '.')
[Double]$Number2 = ($Number2 -join '.')
$Number1 * $Number2
}
用户可以任选一种方式运行:
PS> Multiply-LocalNumber 1.5 9.223
13,8345
PS> Multiply-LocalNumber 1,5 9,223
13,8345
当用户选择使用逗号,PowerShell 实际上将它解释成一个数组。这是为什么脚本将数组用“.”连接的原因,实际上是将数组转换为一个数字。-join
的执行结果是一个字符串,该字符串需要被转换成一个数字,所以一切正常。
当然,这是个有点黑客的技巧,它总比每次首先得指导您的用户必须使用“.”分隔符来得好。
如果您想检测某个信息类似“是否是合法的日期”,以下是一个检测的函数:
function Test-Date
{
param
(
[Parameter(Mandatory=$true)]
$Date
)
(($Date -as [DateTime]) -ne $null)
}
这段代码使用 -as
操作符尝试将输入数据转换为 DateTime
格式。如果转换失败,则结果为 $null
,所以函数可以根据转换的结果返回 $true 或 $false。请注意,-as
操作符使用您的本地 DateTime
格式。