PowerShell 技能连载 - 读取注册表键值和值类型

Get-ItemProperty 可以方便地读取注册表键值,但是无法获得注册表键值的数据类型。

Get-ItemProperty -Path 'HKLM:\Software\Microsoft\Windows NT\CurrentVersion'

以下是通过 .NET 方法的另一种实现,可以获得所有的信息:

PS> Get-RegistryValue 'HKLM\Software\Microsoft\Windows NT\CurrentVersion'

Name                     Type Value
----                     ---- -----
CurrentVersion         String 6.1
CurrentBuild           String 7601
SoftwareType           String System
CurrentType            String Multiprocessor Free
InstallDate             DWord 1326015519
RegisteredOrganization String
RegisteredOwner        String Tobias
SystemRoot             String C:\Windows
InstallationType       String Client
EditionID              String Ultimate
ProductName            String Windows 7 Ultimate
ProductId              String 0042xxx657
DigitalProductId       Binary {164, 0, 0, 0...}
DigitalProductId4      Binary {248, 4, 0, 0...}
CurrentBuildNumber     String 7601
BuildLab               String 7601.win7sp1_gdr.150202-1526
BuildLabEx             String 7601.18741.amd64fre.win7sp1_gdr.150202-1526
BuildGUID              String f974f16b-3e62-4136-a6fb-64fccddecde3
CSDBuildNumber         String 1130
PathName               String C:\Windows

我们需要开发一个 Get-RegistryValue 函数来实现该功能。请注意该函数可传入任意合法的注册表键,并且不需要使用 PowerShell 驱动器号。

function Get-RegistryValue
{
    param
    (
        [Parameter(Mandatory = $true)]
        $RegistryKey
    )

    $key = Get-Item -Path "Registry::$RegistryKey"
    $key.GetValueNames() |
    ForEach-Object {
        $name = $_
        $rv = 1 | Select-Object -Property Name, Type, Value
        $rv.Name = $name
        $rv.Type = $key.GetValueKind($name)
        $rv.Value = $key.GetValue($name)
        $rv
    }
}

PowerShell 技能连载 - 查找电视剧信息

PowerShell 可以查询基于 XML 内容的网站,以下是一个查询电影数据库的例子:

只需要输入您感兴趣的电视剧名称即可。如果您不能直接访问 Internet,可以用 -Proxy 参数指定代理服务器。

#requires -Version 3

$name = 'stargate'
$url = "http://thetvdb.com/api/GetSeries.php?seriesname=$name&language=en"

$page = Invoke-WebRequest -Uri $url <#-Proxy 'http://proxy....:8080' -ProxyUseDefaultCredentials#>
$content = $page.Content


$xml = [XML]$content
$xml.Data.Series | Out-GridView

PowerShell 技能连载 - 比较文件夹内容

要快速比较文件夹内容并且找出只在一个文件夹中存在的文件,请试试以下代码:

$list1 = Get-ChildItem c:\Windows\system32 | Sort-Object -Property Name

$list2 = Get-ChildItem \\server12\c$\windows\system32 | Sort-Object -Property Name


Compare-Object -ReferenceObject $list1 -DifferenceObject $list2 -Property Name |
  Sort-Object -Property Name

该代码属两个文件夹列表,一个来自本机,另一个来自远程计算机。接下来 Compare-Object 命令会挑出只在一个文件夹中存在的文件。

PowerShell 技能连载 - 批量重命名文件

假设您有一整个文件夹的图片文件,并希望它们的名字标准化。

这个脚本演示了如何批量重命名图片文件:

$i = 0

Get-ChildItem -Path c:\pictures -Filter *.jpg |
ForEach-Object {
    $extension = $_.Extension
    $newName = 'pic_{0:d6}{1}' -f  $i, $extension
    $i++
    Rename-Item -Path $_.FullName -NewName $newName
}

文件夹中所有的 JPG 文件都被重命名了。新的文件名是“pic_”加上四位数字。

您可以很容易地修改脚本来重命名其它类型的文件,或是使用其它文件名模板。

PowerShell 技能连载 - 对密码加密

如果您确实需要在脚本中保存一个凭据对象,以下是将一个安全字符串转换为加密文本的方法:

$password = Read-Host -Prompt 'Enter Password' -AsSecureString
$encrypted = $password | ConvertFrom-SecureString
$encrypted | clip.exe
$encrypted

当运行这段代码时,会要求您输入密码。接下来密码会被转换为一系列字符并存入剪贴板中。加密的密钥是您的身份标识加上您的机器标识,所以只能用相同机器的相同用户对密码解密。

下一步,用这段代码可以将您的密码密文转换为凭据对象:

$secret = '01000000d08c9ddf0115d1118c7a00c04fc297eb01000000d4a6c6bfcbbb75418de6e9672d85e73600...996f8365c8c82ea61f94927d3e3b14000000c6aecec683717376f0fb18519f326f6ac9cd89dc'
$username = 'test\user'

$password = $secret | ConvertTo-SecureString

$credential = New-Object -TypeName System.Management.Automation.PSCredential($username, $password)

# example call
Start-Process notepad -Credential $credential -WorkingDirectory c:\

将加密的密码字符串写入脚本中,然后使用指定的用户名来验证身份。

现在,$cred 中保存的凭据对象可以在任何支持 -Credential 参数的 cmdlet 或函数中使用了。

PowerShell 技能连载 - 用口令对文本信息加密

适用于 PowerShell 3.0 及以上版本

在前一个技能中,我们介绍了如何使用 Windows 注册表中的 Windows 产品序列号来加密文本信息。

如果您觉得这种方式不够安全,那么可以使用自己指定的密钥来加密。以下例子演示了如何使用密码作为加密密钥:

$Path = "$env:temp\secret.txt"
$Secret = 'Hello World!'
$Passphrase = 'Some secret key'

$key = [Byte[]]($Passphrase.PadRight(24).Substring(0,24).ToCharArray())

$Secret |
  ConvertTo-SecureString -AsPlainText -Force |
  ConvertFrom-SecureString -Key $key |
  Out-File -FilePath $Path

notepad $Path

要解密一段密文,您需要知道对应的密码:

$Passphrase = Read-Host 'Enter the secret pass phrase'

$Path = "$env:temp\secret.txt"

$key = [Byte[]]($Passphrase.PadRight(24).Substring(0,24).ToCharArray())

try
{
  $decryptedTextSecureString = Get-Content -Path $Path -Raw |
  ConvertTo-SecureString -Key $key -ErrorAction Stop

  $cred = New-Object -TypeName System.Management.Automation.PSCredential('dummy', $decryptedTextSecureString)
  $decryptedText = $cred.GetNetworkCredential().Password
}
catch
{
  $decryptedText = '(wrong key)'
}
"The decrypted secret text: $decryptedText"

PowerShell 技能连载 - 用 Windows 加密信息

适用于 PowerShell 3.0 及以上版本

要存储机密信息,您可以使用 SecureString 对象将其保存到磁盘上。PowerShell 自动使用用户账户作为密钥,所以只有保存该信息的用户可以读取它。

如果您希望该机密信息不绑定到特定的用户,而是绑定到某台机器,您可以使用 Windows 产品序列号作为密钥。请注意这并不是特别安全,因为密钥在 Windows 注册表中时公开可见的。它还有个使用前提是 Windows 是使用合法产品序列号安装的。

以下这段代码接受任意文本信息,然后用 Windows 产品序列号对它进行加密并保存到磁盘上:

$Path = "$env:temp\secret.txt"
$Secret = 'Hello World!'

$regKey = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name DigitalProductID
$encryptionKey = $regKey.DigitalProductID

$Secret |
  ConvertTo-SecureString -AsPlainText -Force |
  ConvertFrom-SecureString -Key ($encryptionKey[0..23]) |
  Out-File -FilePath $Path

notepad $Path

这是对加密的文本进行解密的代码:

$Path = "$env:temp\secret.txt"

$regKey = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name DigitalProductID
$encryptionKey = $regKey.DigitalProductID

$decryptedTextSecureString = Get-Content -Path $Path -Raw |
  ConvertTo-SecureString -Key ($secureKey[0..23])

$cred = New-Object -TypeName System.Management.Automation.PSCredential('dummy', $decryptedTextSecureString)
$decryptedText = $cred.GetNetworkCredential().Password

"The decrypted secret text: $decryptedText"

请注意如何 PSCredential 对象来对密文进行解密并还原出明文的。

PowerShell 技能连载 - 查找 Exchange 邮箱

适用于 Microsoft Exchange 2013

要查看邮箱的个数,只需要使用 Exchange cmdlet 并且用 Measure-Object 来统计结果:

Get-Mailbox –ResultSize Unlimited |
  Measure-Object |
  Select-Object -ExpandProperty Count

类似地,要查看所有共享的邮箱,使用这段代码:

Get-Mailbox –ResultSize Unlimited -RecipientTypeDetails SharedMailbox |
  Measure-Object |
  Select-Object -ExpandProperty Count

若要只查看用户邮箱,需要稍微调整一下:

Get-Mailbox –ResultSize Unlimited -RecipientTypeDetails UserMailbox |
  Measure-Object |
  Select-Object -ExpandProperty Count

PowerShell 技能连载 - 智能参数验证

适用于 PowerShell 2.0 及以上版本

当您用 PowerShell 创建带参数的函数时,请明确地告知 PowerShell 该参数的类型。

这是一个简单的例子,您需要输入一个星期数:

function Get-Weekday
{
  param
  (
    $Weekday
  )

  "You chose $Weekday"
}

用户可以传入任何东西,不仅是正常的星期数:

PS> Get-Weekday -Weekday NoWeekday
You chose NoWeekday

有些时候,您可能会看到用正则表达式实现的验证器:

function Get-Weekday
{
  param
  (
    [ValidatePattern('Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday')]
    $Weekday
  )

  "You chose $Weekday"
}

现在,用户的输入被限定在了这些模式中,如果输入的值不符合正则表达式的模式,PowerShell 将会抛出一个异常。然而,错误信息并不是很有用,并且用户输入的时候并不能享受到智能提示的便利。

一个更好的方法是使用验证集合:

function Get-Weekday
{
  param
  (
    [ValidateSet('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday')]
    $Weekday
  )

  "You chose $Weekday"
}

现在,用户只能输入您允许的值,并且用户在 PowerShell ISE 中输入的时候会获得智能提示信息,显示允许输入的值。

如果您了解您期望值对应的 .NET 的枚举类型,那么可以更简单地将该类型绑定到参数上:

function Get-Weekday
{
  param
  (
    [System.DayOfWeek]
    $Weekday
  )

  "You chose $Weekday"
}