PowerShell 技能连载 - 自动展开和内存消耗

适用于 PowerShell 3.0 及以上版本

在 PowerShell 3.0 中,增加了一个称为“自动回滚”的特性。通过这个特性,您可以这样书写代码:

(Get-ChildItem -Path $env:windir\system32 -Filter *.dll).VersionInfo

这行代码查找 System32 子文件夹下的所有 DLL 文件并且对它们进行迭代,对每个文件返回其 VersionInfo 属性(实际上是 DLL 版本)。在使用自动展开功能之前,您需要手工编写循环语句:

Get-ChildItem -Path $env:windir\system32 -Filter *.dll | ForEach-Object { $_.VersionInfo }

当您运行以上两段代码时,它们返回完全相同的结果。然而,您将立刻发现自动展开特性所带来的代价:它消耗了更多的时间才返回结果。第一行结果出来时可能要消耗 10 秒之多的时间,而“传统”的方法几乎是连续地返回信息。

总体消耗的时间是差不多的。实际上,自动展开特性等价的代码如下:

$data = Get-ChildItem -Path $env:windir\system32 -Filter *.dll
Foreach ($element in $data) { $element.VersionInfo }

自动展开代码更直观,更容易书写手写循环兼容性更好,更快输出结果。

PowerShell 技能连载 - 复制命令行历史

适用于 PowerShell 所有版本

要将 PowerShell 会话中键入过的所有 PowerShell 命令保存下来,请试试这行代码:

(Get-History).CommandLine | clip.exe

它将所有的命令拷贝至剪贴板。然后您就可以将它们粘贴到 PowerShell ISE 并保存为文件。

PowerShell 技能连载 - 获取计算机序列号

适用于 PowerShell 所有版本

在前一个技巧里我们演示了如何通过 DELL 的序列号在线检查保修状态。其它厂家也会提供类似的服务。

这段代码可以读取序列号:

$ComputerName = $env:COMPUTERNAME

$serial = (Get-WmiObject -ComputerName $ComputerName -Class Win32_BIOS).SerialNumber
"Your computer serial is $serial"

PowerShell 技能连载 - 在线检测 DELL 保修

适用于 PowerShell 2.0 及以上版本

如果您拥有一台 DELL 电脑,您可以通过 Web Service 提交电脑的序列号得到授权信息:

$serial = '36GPL41'

$service = New-WebServiceProxy -Uri http://143.166.84.118/services/assetservice.asmx?WSDL
$guid = [Guid]::NewGuid()

$info = $service.GetAssetInformation($guid,'warrantycheck',$serial)
$info.Entitlements

结果可能看起来如下:

$info.Entitlements


ServiceLevelCode        : TS
ServiceLevelDescription : P, ProSupport
Provider                : DELL
StartDate               : 23.03.2004 00:00:00
EndDate                 : 23.03.2007 00:00:00
DaysLeft                : 0
EntitlementType         : Expired

ServiceLevelCode        : ND
ServiceLevelDescription : C, NBD ONSITE
Provider                : UNY
StartDate               : 23.03.2005 00:00:00
EndDate                 : 23.03.2007 00:00:00
DaysLeft                : 0
EntitlementType         : Expired

ServiceLevelCode        : ND
ServiceLevelDescription : C, NBD ONSITE
Provider                : UNY
StartDate               : 23.03.2004 00:00:00
EndDate                 : 24.03.2005 00:00:00
DaysLeft                : 0
EntitlementType         : Expired

这些从 Web Service 返回的信息还包括了其它有用的信息,例如计算机的系统类型:

PS> $info.AssetHeaderData


ServiceTag     : 36GPL41
SystemID       : PLX_PNT_CEL_GX270
Buid           : 11
Region         : Americas
SystemType     : OptiPlex
SystemModel    : GX270
SystemShipDate : 23.03.2004 07:00:00

PowerShell 技能连载 - 用 Cmdlet 来管理 MSI 安装包

适用于 PowerShell 2.0 及以上版本

需要管理 MSI 安装包的朋友可以从这个开源项目中受益:http://psmsi.codeplex.com/

只需要下载 PowerShell 模块——它自己包含了一个安装包。请确保在安装它之前对 MSI 文件进行解锁。否则,Windows 可能会拒绝安装它。

不幸的是,这个模块将它自己安装到一个很特殊的地方(AppData\Local\Apps...),并且扩展了 $env:PSModulePath 环境变量,所以 PowerShell 可以找到这个模块。这是为什么您在安装完模块之后需要重启 PowerShell 的原因,因为 PowerShell 不能自动感知到 $env:PSModulePath 发生了改变。

这是获取新的 MSI 相关 cmdlet 的方法:

PS> Get-Command -Module MSI

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Function        Get-MSIComponentState                              MSI
Function        Get-MSISharedComponentInfo                         MSI
Function        Install-MSIAdvertisedFeature                       MSI
Cmdlet          Add-MSISource                                      MSI
Cmdlet          Clear-MSISource                                    MSI
Cmdlet          Edit-MSIPackage                                    MSI
Cmdlet          Export-MSIPatchXml                                 MSI
Cmdlet          Get-MSIComponentInfo                               MSI
Cmdlet          Get-MSIFeatureInfo                                 MSI
Cmdlet          Get-MSIFileHash                                    MSI
Cmdlet          Get-MSIFileType                                    MSI
Cmdlet          Get-MSILoggingPolicy                               MSI
Cmdlet          Get-MSIPatchInfo                                   MSI
Cmdlet          Get-MSIPatchSequence                               MSI
Cmdlet          Get-MSIProductInfo                                 MSI
Cmdlet          Get-MSIProperty                                    MSI
Cmdlet          Get-MSIRelatedProductInfo                          MSI
Cmdlet          Get-MSISource                                      MSI
Cmdlet          Get-MSISummaryInfo                                 MSI
Cmdlet          Get-MSITable                                       MSI
Cmdlet          Install-MSIPatch                                   MSI
Cmdlet          Install-MSIProduct                                 MSI
Cmdlet          Measure-MSIProduct                                 MSI
Cmdlet          Remove-MSILoggingPolicy                            MSI
Cmdlet          Remove-MSISource                                   MSI
Cmdlet          Repair-MSIProduct                                  MSI
Cmdlet          Set-MSILoggingPolicy                               MSI
Cmdlet          Test-MSIProduct                                    MSI
Cmdlet          Uninstall-MSIPatch                                 MSI
Cmdlet          Uninstall-MSIProduct                               MSI

PowerShell 技能连载 - 读取多行文本

适用于 PowerShell 3.0 及以上版本

有些时候您偶然会见到类似这样的技巧:

$FilePath = "$env:SystemRoot\WindowsUpdate.log"

$ContentsWithLinebreaks = (Get-Content $FilePath) -join "`r`n"

您能否出猜出它的用意?Get-Content 缺省情况下返回由一行一行组成的字符串数组,然后 -join 操作符将该数组转化为一个字符串。

从 PowerShell 3.0 开始,Get-Content 多了一个参数:-Raw。它比起刚才的方法高效的多,并且可以得到相同的结果:

$FilePath = "$env:SystemRoot\WindowsUpdate.log"

$ContentsWithLinebreaks = (Get-Content $FilePath) -join "`r`n"

$ContentsWithLinebreaks2 = Get-Content $FilePath -Raw

$ContentsWithLinebreaks -eq $ContentsWithLinebreaks2

当您使用这段代码时,会发现 $ontentWithLinebreaks$ContentWithLinebreaks2 是不同的。唯一的区别是在 $ContentsWithLinebreaks2 尾部有一个换行符:

PS> $ContentsWithLinebreaks -eq $ContentsWithLinebreaks2.TrimEnd("`r`n")
True

PS>

PowerShell 技能连载 - 要求管理员权限

适用于 PowerShell 4.0 及以上版本

如果您知道某个脚本需要管理员权限,只需要一个简单的 #requres 语句就可以确保符合该需求的才可以运行:

#requires -version 4.0
#requires –runasadministrator


'I am Admin!'

如果这个脚本没有使用管理员身份运行,它将显示一个有意义错误提示信息,说明它为何无法运行。

实际上,在这个例子中您可以看到两条 #requires 语句。第一条确保该脚本至少运行在 PowerShell 4.0 以上的环境中,这是第二条 #requires 的先决条件。它是由 PowerShell 4.0 引入的,不支持 PowerShell 更低的版本。

所以最好不要在 PowerShell 3.0 或更早的环境中使用这个技术。在那些环境中,您还是需要手工确认脚本是否拥有管理员权限。

PowerShell 技能连载 - 分析并移除打印任务

适用于 Windows 8.1 或 Server 2012 R2

Windows 8.1 和 Server 2012 R2 引入了一个名为“PrintManagement”的模块。它包含了管理本地和远程打印机所需的所有 cmdlet。

在前一个技能中我们演示了如何读取打印任务。每个打印任务都有一个 JobStatus 属性告诉您该 PrintJob 是否成功完成。

可以通过这种方式获取所有的状态码:

PS> Import-Module PrintManagement

PS> [Microsoft.PowerShell.Cmdletization.GeneratedTypes.PrintJob.JobStatus]::GetNames([Microsoft.PowerShell.Cmdletization.GeneratedTypes.PrintJob.JobStatus])
Normal
Paused
Error
Deleting
Spooling
Printing
Offline
PaperOut
Printed
Deleted
Blocked
UserIntervention
Restarted
Complete
Retained
RenderingLocally

接下来,您可以过滤已有的打印任务。并且,比如打印出所有已完成或有错误的打印任务。这段代码将列出所有有错误或已完成的打印任务:

$ComputerName = $env:COMPUTERNAME

Get-Printer -ComputerName $ComputerName |  ForEach-Object {
  Get-PrintJob -PrinterName $_.Name -ComputerName $ComputerName |
    Where-Object { $_.JobStatus -eq 'Complete' -or $_.JobStatus -eq 'Error' -or $_.JobStatus -eq 'Printed'}
 }

要移除这些打印任务,只需要加上 Remove-PrintJob 命令:

$ComputerName = $env:COMPUTERNAME

Get-Printer -ComputerName $ComputerName |  ForEach-Object {
  Get-PrintJob -PrinterName $_.Name -ComputerName $ComputerName |
    Where-Object { $_.JobStatus -eq 'Complete' -or $_.JobStatus -eq 'Error' -or $_.JobStatus -eq 'Printed'}
 } |
 Remove-PrintJob -CimSession $ComputerName

PowerShell 技能连载 - 列出所有打印任务

适用于 Windows 8.1 或 Server 2012 R2

Windows 8.1 和 Server 2012 R2 引入了一个名为“PrintManagement”的模块。它包含了管理本地和远程打印机所需的所有 cmdlet。

要列出指定计算机的所有打印任务,首先确定可用的打印机,然后用循环取出每个打印机的打印任务。这实际做起来十分简单:

$ComputerName = $env:COMPUTERNAME

Get-Printer -ComputerName $ComputerName |  ForEach-Object {
  Get-PrintJob -PrinterName $_.Name -ComputerName $ComputerName
 }

如果该代码返回空,那么说明没有打印任务(或者您没有读取它们的权限)。

PowerShell 技能连载 - 远程更新组策略

适用于 Windows 8.1 或 Server 2012 R2

要更新远程计算机上的组策略设置,请使用 Invoke-GPUpdate,并且传入希望更新设置的计算机名。

Invoke-GPUpdate 在远程计算机上创建“gpupdate”计划任务。您可以使用 –RandomDelayInMinutes 指定一个 0 至 44640 分钟(31 天)之间的值。该 cmdlet 将使用一个随机的时间因子来避免网络阻塞。