PowerShell 技能连载 - 访问非 Microsoft LDAP 服务

适用于 PowerShell 所有版本

Microsoft 和 Dell 提供了一些 Active Directory 的免费 cmdlet,分别是 RSAT 工具和 Quest 的一部分。它们使访问域控制器变得更简单。

要访问一个非 Microsoft 的 LDAP 服务器,由于没有现成的 cmdlet,所以可以使用 .NET 框架的功能。

以下是一些示例代码,演示了如何连接这种 LDAP 服务器,提交 LDAP 查询,并且获取信息。

该脚本假设 LDAP 服务器架设在 192.168.1.1 的 389 端口,是“mycompany.com”域的一部分,有一个名为“SomeGroup”的工作组。该脚本列出该工作组的用户账户:

$LDAPDirectoryService = '192.168.1.1:389'
$DomainDN = 'dc=mycompany,dc=com'
$LDAPFilter = '(&(cn=SomeGroup))'


$null = [System.Reflection.Assembly]::LoadWithPartialName('System.DirectoryServices.Protocols')
$null = [System.Reflection.Assembly]::LoadWithPartialName('System.Net')
$LDAPServer = New-Object System.DirectoryServices.Protocols.LdapConnection $LDAPDirectoryService
$LDAPServer.AuthType = [System.DirectoryServices.Protocols.AuthType]::Anonymous

$LDAPServer.SessionOptions.ProtocolVersion = 3
$LDAPServer.SessionOptions.SecureSocketLayer =$false

$Scope = [System.DirectoryServices.Protocols.SearchScope]::Subtree
$AttributeList = @('*')

$SearchRequest = New-Object System.DirectoryServices.Protocols.SearchRequest -ArgumentList $DomainDN,$LDAPFilter,$Scope,$AttributeList

$groups = $LDAPServer.SendRequest($SearchRequest)

foreach ($group in $groups.Entries)
{
  $users=$group.attributes['memberUid'].GetValues('string')
  foreach ($user in $users) {
    Write-Host $user
  }
}

PowerShell 技能连载 - 查找只读型和常量型变量

适用于 PowerShell 所有版本

有些变量是受保护且不可改变的。要查这些变量,请看如下代码:

Get-Variable |
  Where-Object { $_.Options -like '*Constant*' -or $_.Options -like '*ReadOnly*' } |
  Select-Object -Property Name, Options, Description

执行的结果类似这样:

Name                                           Options Description
----                                           ------- -----------
?                                   ReadOnly, AllScope Status des letzten Befehls
ConsoleFileName                     ReadOnly, AllScope Name der aktuellen Kons...
Error                                         Constant
ExecutionContext                    Constant, AllScope Die für Cmdlets verfügb...
false                               Constant, AllScope Boolean False
HOME                                ReadOnly, AllScope Ordner mit dem Profil d...
Host                                Constant, AllScope Ein Verweis auf den Hos...
PID                                 Constant, AllScope Aktuelle Prozess-ID
PSCulture                           ReadOnly, AllScope Die Kultur der aktuelle...
PSHOME                              Constant, AllScope Der übergeordnete Ordne...
psISE                                         Constant
PSUICulture                         ReadOnly, AllScope Die Benutzeroberflächen...
psUnsupportedConsoleAppl...                   Constant
PSVersionTable                      Constant, AllScope Versionsinformationen f...
ShellId                             Constant, AllScope "ShellID" gibt die aktu...
true                                Constant, AllScope Boolean True

有意思的地方是如何用 Where-Object 来过滤这些变量。这段代码使用了字符串对比和 -like。这是因为变量选项是各种标志,并且标志可以组合使用。通过使用 -like 和占位符,您可以基本安全地用您希望的标志来过滤,即便设置了其它标志。

PowerShell 技能连载 - 只读及强类型变量

适用于 PowerShell 所有版本

要让 PowerShell 脚本更鲁棒,您可以针对脚本变量编写一系列约束条件。

这么做的效果是,PowerShell 将会为您监控这些约束条件,并且如果某些条件不满足,将抛出错误。

第一个约束条件是传入的数据类型必须和期望的相符。将数据类型放在方括号中,然后将它放在变量前面。这将会使一个通用类型的变量转换为一个强类型的变量。

PS> $profile.AllUsersAllHosts
C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1

PS> [int]$ID = 12

PS> $ID = 17

PS> $ID = '19'

PS> $ID = 'wrong'
Cannot convert type "string"...

请看 $ID 变量是如何成为一个只能存储 Integer 数据的变量。望您将一个非 Integer 值,(例如 ‘19’)传入时,它将自动转换成 Integer 类型。如果无法转换(例如 ‘wrong’),PowerShell 将会抛出一个错误。

下一个约束条件是“只读”状态。如果您确信某个变量在脚本的某一部分中不可被更改,请将它保住为只读。任何试图改变该变量的操作都会触发一个 PowerShell 异常:

PS> $a = 1

PS> $a = 100

PS> Set-Variable a -Option ReadOnly

PS> $a
100

PS> $a = 200
Cannot  overwrite variable.

PS> Set-Variable a -Option None -Force

PS> $a = 212

请注意写保护如何打开和关闭。要将写保护关闭,只需要将变量开关设为“None”,并且别忘了 -Force 开关。

如您所见,“ReadOnly”选项是一个软保护开关。您可以控制它开和关。在前一个技能中,您学到了“Constant”选项。Constant 的意思如它们的名字:常量。和“ReadOnly”不同,常量无法变回可写状态。

PowerShell 技能连载 - 使用常量

适用于 PowerShell 所有版本

PowerShell 中的变量是不稳定的。您可以覆盖或是删除它们——除非创建常量。

常量只能在还不存在该常量的时候创建。这行代码创建了一个名为“_connotChange_”,值为 1 的常量。

New-Variable -Name cannotChange -Value 1 -Option Constant

在 PowerShell 运行期间,无法删除这个变量。该变量被绑定到当前的 PowerShell 会话。常量可以用在您不希望改变的敏感信息上。

您可以在您的主配置文件路径上定义常量,例如:

PS> $profile.AllUsersAllHosts
C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1

如果该文件存在,PowerShell 将会在任何实例启动之前先执行它。如果您在此定义了常量——例如您的公司名、重要的服务器列表等——该信息将在所有 PowerShell 宿主内有效,而且无法被覆盖。

PowerShell 技能连载 - 在脚本中包含资源

适用于 PowerShell 3.0 及以上版本

如果您的脚本需要额外的资源,比如说服务器名列表,图片文件,或是其它内容,那么请确保您的脚本可随处使用。

千万别使用绝对路径来定位资源文件。应使用 PowerShell 3.0 开始提供的 $PSScriptRoot(在 PowerShell 2.0 中,$PSScriptRoot 只能在模块中使用)。

$picture = "$PSScriptRoot\Resources\picture.png"
Test-Path -Path $picture

$data = "$PSScriptRoot\Resources\somedata.txt"
Get-Content -Path $data

$PSScriptRoot 总是指向脚本所在的目录(所以如果脚本尚未保存或是以交互式查询该变量,得到的结果是空)。

PowerShell 技能连载 - 在 PowerShell ISE 中使用“more”

适用于 PowerShell ISE

在 PowerShell 控制台中,您可以将命令通过管道输出到旧式的“more.com”命令中,或者更高级一些的 Out-Host -Paging 中。这将分页显示数据,每按一个键翻一页:

PS> Get-Process | more
PS> Get-Process | Out-Host -Paging

在 PowerShell ISE 中,这些都不支持。这是因为 ISE 没有控制台,所以没有“可见的行数”概念,只有一个无限的文本缓冲区。

要避免大量的结果刷屏问题,只需要自己创建一个兼容 ISE 的“more”命令:

function Out-More
{
    param
    (
        $Lines = 10,

        [Parameter(ValueFromPipeline=$true)]
        $InputObject
    )

    begin
    {
        $counter = 0
    }

    process
    {
        $counter++
        if ($counter -ge $Lines)
        {
            $counter = 0
            Write-Host 'Press ENTER to continue' -ForegroundColor Yellow
            Read-Host
        }
        $InputObject
    }
}

将您的结果通过管道输出到 Out-More,命令,并使用 -Lines 来指定接收到多少行以后暂停:

PS> Get-Process | Out-More -Lines 4

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    100       9     1436       1592    51            6896 adb
    307      42    22332      12940   123            1424 AllShareFrameworkDMS
     42       4     1396       1284    19            1404 AllShareFrameworkManagerDMS
Press ENTER to continue


     81       7     1004        724    43            1388 armsvc
    202      25    50376      55320   234     8,06  13720 chrome
   1131      65    68672      93892   361   116,73  13964 chrome
    199      24    53008      52700   225     5,56  14768 chrome
Press ENTER to continue


    248      26    31348      44984   239    44,31  15404 chrome
    173      20    23756      25540   179     1,27  16492 chrome
    190      22    36316      39208   207     2,81  16508 chrome
    184      23    41800      44212   223     1,77  17244 chrome
Press ENTER to continue

PowerShell 技能连载 - 从注册表中读取用户配置文件

适用于 PowerShell 所有版本

要查看哪个用户在您的机器上拥有(本地)配置文件,以及配置文件位于什么位置,请使用这段代码:

$path = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*'

Get-ItemProperty -Path $path |
  Select-Object -Property PSChildName, ProfileImagePath

它将自动枚举配置文件列表中的所有键,并返回用户 SID 和配置文件的路径:

PSChildName                              ProfileImagePath
-----------                              ----------------
S-1-5-18                                 C:\WINDOWS\system32\config\systemprofile
S-1-5-19                                 C:\Windows\ServiceProfiles\LocalService
S-1-5-20                                 C:\Windows\ServiceProfiles\NetworkSer...
S-1-5-21-1907506615-3936657230-268413... C:\Users\Tobias
S-1-5-80-3880006512-4290199581-164872... C:\Users\MSSQL$SQLEXPRESS

PowerShell 技能连载 - 从注册表中读取文件扩展名关联(第二部分)

适用于 PowerShell 所有版本

在前一个技能里您已学到了如何用单行代码读取多个注册表键。在第二部分中,请试试这段单行代码:

$lookup = Get-ItemProperty Registry::HKCR\.[a-f]?? |
  Select-Object -Property PSChildName, '(default)', ContentType, PerceivedType |
  Group-Object -Property PSChildName -AsHashTable -AsString

这段代码读取注册表 HKCR 中所有以点开头,接下来是三个字母,并且第一个字母必须是 a-f 的键——它的作用是读取所有以 a-f 开头,并且必须是 3 个字符的文件扩展名。

另外,结果通过管道输出到 Group-Object,并且“PSChildName”属性被用作哈希表的键名。

PSChildName 总是返回注册表的键名,在这个例子中代表的是文件的扩展名。

当您运行这行代码时,您可以查询任何已注册的文件扩展名:

PS> $lookup.'.avi'

PSChildName         (default)           ContentType         PerceivedType
-----------         ---------           -----------         -------------
.avi                WMP11.AssocFile.AVI                     video



PS> $lookup.'.fon'

PSChildName         (default)           ContentType         PerceivedType
-----------         ---------           -----------         -------------
.fon                fonfile

请注意这行代码限制了只包括 a-f 开头、三个字母的扩展名。要获取所有的文件扩展名,请使用这个路径:

Registry::HKCR\.*

PowerShell 技能连载 - 从注册表中读取文件扩展名关联(第一部分)

适用于 PowerShell 所有版本

PowerShell 代码可以写得十分紧凑。以下是一个从 Windows 注册表中读取所有文件扩展名关联的单行代码:

Get-ItemProperty Registry::HKCR\.* |
  Select-Object -Property PSChildName, '(default)', ContentType, PerceivedType

请注意这些技巧:Get-ItemProperty 使用了名为“Registry::”的 provider,而不是 PowerShell 提供的注册表驱动器。通过这种方式,您可以使用默认的注册表路径,并且可以存取类似 HKEY_CLASSES_ROOT 这样没有驱动器的 hive。

请注意 Select-Object 如何选择获取注册表值。这里有两个特殊的名字:“(default)”总是代表缺省的注册表值,并且“PSChildName”总是代表当前读取的注册表键名。

由于路径名中的“*”符,该命令将自动读取 HKCR 路径中以点开头的所有键。

PowerShell 技能连载 - 根据类型对数据排序

适用于 PowerShell 所有版本

Sort-Object 是一站式的排序解决方案。如果要对简单数据类型排序,只需要将它通过管道传递给 Sort-Object。如果要对复杂数据类型排序,则需要指定排序所使用的属性:

# sorting primitive data
1,5,2,1,6,3,12,6 | Sort-Object -Unique

# sorting object data
Get-ChildItem -Path c:\windows | Sort-Object –Property name

由于对象的天生原因,PowerShell 自动选择排序的算法。但如果您需要更多的控制呢?

只需要传入一个代码块。在代码块中,$_ 代表需要排序的对象。您可以将它转型为任何期望的类型:

# sorting string as numbers
'1','5','3a','12','6' | Sort-Object -Property { $_ -as [int]  }

# sorting IPv4 addresses as versions
'1.2.3.4', '10.1.2.3', '100.4.2.1', '2.3.4.5', '9.10.11.12' |
  Sort-Object -Property { [version] $_ }