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] $_ }

PowerShell 技能连载 - 用 PowerShell 打开网页

适用于 PowerShell 所有版本

假设您希望每天开始时,就用浏览器打开您最喜欢的网站。PowerShell 可以简单地实现这个需求。不过,这要看您是否喜欢自动打开网页。

使用 Start-Process 的时候,您可以通过参数指定选用哪个浏览器,以及提交的 URL 地址:

# starts with a specific browser
Start-Process -FilePath iexplore -ArgumentList www.powershellmagazine.com

这将在 Internet Explorer 中打开网站,而不是选用缺省的浏览器设置。

不过,它总是打开一个新的浏览器实例。请看另一种实现方式:

# starts with default browser and adds to open browser
Start-Process -FilePath www.tagesschau.de

这段代码中,我们将一个 URL 当做一个“可执行程序”,Windows 就会使用缺省的浏览器。如果缺省浏览器已经打开了,将在其中打开一个新的标签页,而不是打开一个新的浏览器窗口。

要让 PowerShell 在启动时自动打开网页,请确保 $profile 指向的文件路径存在。下一步,打开该脚本,并添加启动命令。

PowerShell 技能连载 - 检查网站的响应(和执行时间)

适用于 PowerShell 3.0 及以上版本

有些时候了解一个命令的执行时间是十分重要的。例如,要监控网站的响应时间,您可以使用 Invoke-WebRequest。用 Measure-Command 来测量执行的耗时。

$url = 'http://www.powershell.com'


# track execution time:
$timeTaken = Measure-Command -Expression {
  $site = Invoke-WebRequest -Uri $url
}

$milliseconds = $timeTaken.TotalMilliseconds

$milliseconds = [Math]::Round($milliseconds, 1)

"This took $milliseconds ms to execute"

它返回一个 TimeSapn 对象,该对象包含了一个 “TotalMilliseconds“ 属性。使用 “Math“ 类提供的 Round() 方法将结果四舍五入。在这例子中,毫秒值被精确到小数点后 1 位。