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

您可以使用 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

PowerShell 技能连载 - 读取注册表的可扩充字符串值

当您读取一个“可扩充字符串”类型的注册表值时,它将自动展开文本中的所有环境变量值。

这个例子将从注册表中读取系统设备路径:

$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 技能连载 - 设置(及删除)环境变量

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 更新 Oray 花生壳动态 IP

花生壳oray 公司提供的 DDNS 客户端。官方的客户端庞大臃肿:

不过好在花生壳开放了基于 http 的 API。这样我们可以很容易地用 PowerShell 实现更新动态 IP 的功能:

param (
    $UserName = 'xxx',
    $Password = 'yyy',
    $HostName,
    $IP
)

function Get-ExternalIP {
    #(Invoke-WebRequest 'http://myip.dnsomatic.com' -UseBasicParsing).Content
    ((Invoke-WebRequest 'http://ddns.oray.com/checkip').ParsedHtml.body.innerText -split ':')[1].Trim()
}

function Update-OrayDdns {
    param (
        [parameter(Mandatory = $true)]
        [string]$UserName,

        [parameter(Mandatory = $true)]
        [string]$Password,

        [parameter(HelpMessage = '需要更新的域名,此域名必须是开通花生壳服务。多个域名使用,分隔,默认为空,则更新护照下所有激活的域名。')]
        [string]$HostName,

        [parameter(HelpMessage = '需要更新的IP地址,可以不填。如果不指定,则由服务器获取到的IP地址为准。')]
        [string]$IP
    )

    $request = 'http://ddns.oray.com/ph/update?hostname={0}' -f ($HostName -join ',')
    if ($IP) {
        $request = $request + '&myip=' + $IP
    }
    $encoded =  [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($UserName+":"+$Password ))
    $headers = @{Authorization = "Basic "+$encoded}
    $response = Invoke-WebRequest $request -Headers $headers -UseBasicParsing

    $codes = @{
        good = '更新成功,域名的IP地址已经更新。'
        nochg = '更新成功,但没有改变IP。一般这种情况为本次提交的IP跟上一次的一样。'
        notfqdn = '未有激活花生壳的域名。'
        nohost = '域名不存在或未激活花生壳。'
        abuse = '请求失败,频繁请求或验证失败时会出现。'
        '!donator' = '表示此功能需要付费用户才能使用,如https。'
        911 = '系统错误'
    }

    $code = ($response.Content -split ' ')[0]
    $message = $codes[$code]

    if ($code -eq 'good' -or $code -eq 'nochg') {
        Write-Output $message
    } elseif ($code -eq 'notfqdn' -or $code -eq 'nohost') {
        Write-Warning $message
    } else {
        Write-Error $message
    }
}

Update-OrayDdns $UserName $Password $HostName

您也可以从这里 下载 写好的脚本。

PowerShell 技能连载 - 带对话框的必选参数

通常地,当您将一个函数参数标记为“必选的”,如果用户遗漏了这个参数,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 在输入输出时用的是不同的十进制分隔符——这也许会导致脚本用户产生混淆。

当您输入信息时,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 的执行结果是一个字符串,该字符串需要被转换成一个数字,所以一切正常。

当然,这是个有点黑客的技巧,它总比每次首先得指导您的用户必须使用“.”分隔符来得好。

PowerShell 技能连载 - 检测合法的时间

如果您想检测某个信息类似“是否是合法的日期”,以下是一个检测的函数:

function Test-Date
{
    param
    (
        [Parameter(Mandatory=$true)]
        $Date
    )

    (($Date -as [DateTime]) -ne $null)
}

这段代码使用 -as 操作符尝试将输入数据转换为 DateTime 格式。如果转换失败,则结果为 $null,所以函数可以根据转换的结果返回 $true 或 $false。请注意,-as 操作符使用您的本地 DateTime 格式。

PowerShell 技能连载 - 朗读英文和德文(以及西班牙文,或您指定的语言)

Windows 8 是第一个完整支持本地化的文本到语音引擎的操作系统。所以您现在可以用 PowerShell 来朗读(以及咒骂)。

同时,操作系统永远有英文引擎,所以您的计算机拥有两种语言能力。

以下是一个用于德文系统的示例脚本(它可以很容易改为您的地域)。只需要修改语言 ID 即可(例如“de-de”代表德文),就可以让 Windows 说另一种语言。

请注意,在 Windows 8 之前,只附带了英文引擎。在 Windows 8 中,您可以使用您的本地语言。其它语言不可用。

$speaker = New-Object -ComObject SAPI.SpVoice
$speaker.Voice = $speaker.GetVoices() | Where-Object { $_.ID -like '*de-de*'}
$null = $speaker.Speak('Ich spreche Deutsch')
$speaker.Voice = $speaker.GetVoices() | Where-Object { $_.ID -like '*en-us*'}
$speaker.Speak('But I can of course also speak English.')

PowerShell 技能连载 - 单行内为多个变量赋值

当您将某个值赋给一个变量时,您可以用括号把表达式括起来。这个表达式还将返回该数值。我们看看它的样子:

$a = Get-Service
($a = Get-Service)

见到它们的区别了吗?第二行不仅将 Get-Service 的结果赋值给一个变量,而且将把结果输出至控制台。

实际上您也可以利用上第二行的结果。请看如下代码:

$b = ($a = Get-Service).Name
$a
$b

这将把所有的服务赋值给 $a,并把所有的服务名称赋值给 $b。

再次地,您可以将这个结果再用括号括起来,以供下次继续复用这个结果:

$c = ($b = ($a = Get-Service).Name).ToUpper()
$a
$b
$c

现在 $c 将包含所有大写形式的服务名。很另类的写法。

PowerShell 技能连载 - Ping 主机

有很多种方法可供您 ping 主机。以下是一个简单的将传统的 ping.exe 结合进您的脚本的方法:

function Test-Ping
{
    param([Parameter(ValueFromPipeline=$true)]$Name)

    process {
      $null = ping.exe $Name -n 1 -w 1000
      if($LASTEXITCODE -eq 0) { $Name }
    }
}

Test-Ping 接受一个主机名或 IP 地址作为参数并且返回 ping 是否成功。通过这种方法,您可以传入一个大的主机或 IP 地址列表,然后获得在线的结果:

'??','127.0.0.1','localhost','notthere',$env:COMPUTERNAME | Test-Online