PowerShell 技能连载 - 发生了什么?

经常地,您需要用 PowerShell 来获取数据,并且您需要提取信息的一部分并且把它们用于报表。类似如下:

$serial = Get-WmiObject -Class Win32_OperatingSystem |
  Select-Object -Property SerialNumber

"Serial Number is $serial"

但是上述代码产生的结果如下:

Serial Number is @{SerialNumber=00261-30000-00000-AA825}

当您查看 $serial 的值,它看起来似乎很正常:

PS> $serial

SerialNumber
------------
00261-30000-00000-AA825

但问题出在列头(译者注:我们只需要 SerialNumber 的值,而不是需要一个包含 SerialNumber 属性的临时对象)。您可以用 Select-Object 只选出一列,用 -ExpandProperty 而不是 -Property 就可以消除列头:

$serial = Get-WmiObject -Class Win32_OperatingSystem |
  Select-Object -ExpandProperty SerialNumber

"Serial Number is $serial"

现在,一切正常了:

Serial Number is 00261-30000-00000-AA825

PowerShell 技能连载 - 密码混淆器脚本

曾经需要将密码保存在脚本中?曾经需要自动弹出一个身份验证对话框?对于前者,将密码和其它身份信息存储在脚本中是很糟糕的对于后者,如果您这么做了的话,至少能使黑客更难于窃取信息。

以下是一个脚本生成器。运行它,并且输入一个域/用户名和密码,脚本生成器会为您生成一段新脚本。

$pwd = Read-Host 'Enter Password'
$user = Read-Host 'Enter Username'
$key = 1..32 |
  ForEach-Object { Get-Random -Maximum 256 }

$pwdencrypted = $pwd |
  ConvertTo-SecureString -AsPlainText -Force |
  ConvertFrom-SecureString -Key $key

$text = @()
$text += '$password = "{0}"' -f ($pwdencrypted -join ' ')
$text += '$key = "{0}"' -f ($key -join ' ')
$text += '$passwordSecure = ConvertTo-SecureString -String $password -Key ([Byte[]]$key.Split(" "))'
$text += '$cred = New-Object system.Management.Automation.PSCredential("{0}", $passwordSecure)' -f $user
$text += '$cred'

$newFile = $psise.CurrentPowerShellTab.Files.Add()
$newFile.Editor.Text = $text | Out-String

这段脚本包含混淆过的密码脚本,看起来大概类似这样:

$password = "76492d1116743f0423413b16050a5345MgB8AFcAMABGAEIANAB1AGEAdQA3ADUASABhAE0AMgBNADUAUwBnAFYAYQA1AEEAPQA9AHwAMgAyAGIAZgA1ADUAZgA0ADIANAA0ADUANwA2ADAAMgA5ADkAZAAxAGUANwA4ADUAZQA4ADkAZAA1AGMAMAA2AA=="
$key = "246 185 95 207 87 105 146 74 99 163 58 194 93 229 80 241 160 35 68 220 130 193 84 113 122 155 208 49 152 86 85 178"
$passwordSecure = ConvertTo-SecureString -String $password -Key ([Byte[]]$key.Split(" "))
$cred = New-Object system.Management.Automation.PSCredential("test\tobias", $passwordSecure)
$cred

当您运行它,它将生成一个 Credential 对象,您可以立即将它用于身份验证。只要将它传给一个需要 Credential 对象的形参即可。

再强调一下,这并不是安全的。但是要想获取密码的明文还需要更多点知识才行。

快速替换文本文件中的字符串

不用开什么vim、emac、UltraEdit、Eclipse之类的编辑器了,PowerShell可以帮助手无寸铁的您快速地替换文本文件中的字符串:

dir *.txt -Recurse | % {
    (gc $_ -Raw) | % { $_ `
        -creplace '111', 'AAA' `
        -creplace '222', 'BBB' `
        -creplace '333', 'CCC'
    } | sc $_
}

注意 -creplace 区分大小写,-replace 不区分大小写。并且它们支持正则表达式!

在PowerShell中以管理员身份运行程序

对于已知的需要以管理员身份运行的命令,我们可以通过这个 Invoke-Admin 函数运行。这个函数确保以管理员身份运行一个程序。如果不是以管理员身份运行,则将弹出 UAC 对话框。

function Invoke-Admin() {
    param ( [string]$program = $(throw "Please specify a program" ),
            [string]$argumentString = "",
            [switch]$waitForExit )

    $psi = new-object "Diagnostics.ProcessStartInfo"
    $psi.FileName = $program
    $psi.Arguments = $argumentString
    $psi.Verb = "runas"
    $proc = [Diagnostics.Process]::Start($psi)
    if ( $waitForExit ) {
        $proc.WaitForExit();
    }
}

来源:Showing the UAC prompt in PowerShell if the action requires elevation

PowerShell 技能连载 - 检查磁盘分区和数据块大小

WMI 是一个装满信息的宝库。以下这行代码将读取本地分区以及它们的数据块大小信息:

Get-WmiObject -Class Win32_Diskpartition  |
  Select-Object -Property __Server, Caption, BlockSize

使用 Get-WmiObject-ComputerName 参数可以对一台或多台机器远程执行同样的操作。

要查看其它所有的 WMI 类,您可以替换掉 Win32_DiskPartition,试试以下的代码:

Get-WmiObject -Class Win32_* -List |
  Where-Object { ($_.Qualifiers | Select-Object -ExpandProperty Name) -notcontains 'Association' } |
  Where-Object { $_.Name -notlike '*_Perf*' }

PowerShell 技能连载 - 将Excel导出的CSV转换为UTF-8编码

当您导出 Microsoft Excel 数据表到 CSV 文件时,Excel缺省将保存为 ANSI 编码的 CSV 文件。这是很糟糕的,因为当您用 Import-Csv 导入数据到 PowerShell 中时,特殊字符将会截断(译者注:例如中文出现乱码)。

要确保特殊字符不会丢失,您必须确保导入数据之前 CSV 文件采用的是 UTF-8 编码:

$Path = 'c:\temp\somedata.csv'
(Get-Content -Path $Path) | Set-Content -Path $Path -Encoding UTF8

PowerShell 技能连载 - 查找所有用户脚本

有些时候我们会疑惑当 PowerShell 启动的时候,将执行哪些启动脚本。它们数量很多,而且各不相同,要看您运行的是 PowerShell 控制台,ISE,还是其他宿主。

然而,了解您的用户脚本是十分重要的。它们决定了应用到 PowerShell 环境的配置。

这个 Get-PSProfileStatus 函数列出了所有宿主(PowerShell 环境)可能用到的的启动脚本。它也显示了哪些脚本是物理存在的。

function Get-PSProfileStatus
{
    $profile |
      Get-Member -MemberType NoteProperty |
      Select-Object -ExpandProperty Name |
      ForEach-Object {
        $_, (Split-Path $profile.$_ -Leaf), (Split-Path $profile.$_),
                              (Test-Path -Path $profile.$_) -join ',' |
          ConvertFrom-Csv -Header Profile, FileName, FolderName, Present
        }
}

Get-PSProfileStatus

结果看起来类似这样:

将结果用管道输出到 Out-GridView 来查看,避免截断字符被截断:

Get-PSProfileStatus | Out-GridView

PowerShell 技能连载 - 通过CSV创建对象

有多种方法可以创建自定义对象。以下是一种创新的办法,在很多种场景中都很有效:创建一个逗号分隔,每行表示一个值的列表文本,然后用 ConvertFrom-Csv 来创建对象:

for($x=0; $x -lt 20; $x++)
{
    ($x,(Get-Random),(Get-Date) -join ',') | ConvertFrom-Csv -Header ID, RandomNumber, Date
}

不过,这种做法效率并不是很高。还有三种其它方法可以用来创建对象。分别用 Measure-Command 测量创建 2000 个对象所消耗的时间:

Measure-Command {
    for($x=0; $x -lt 2000; $x++)
    {
        ($x,(Get-Random),(Get-Date) -join ',') | ConvertFrom-Csv -Header ID, RandomNumber, Date
    }
}

Measure-Command {
    for($x=0; $x -lt 2000; $x++)
    {
        $obj = 1 | Select-Object -Property ID, RandomNumber, Date
        $obj.ID = $x
        $obj.RandomNumber = Get-Random
        $obj.Date = Get-Date
        $obj
    }
}

Measure-Command {
    for($x=0; $x -lt 2000; $x++)
    {
        [PSObject]@{
            ID = $x
            RandomNumber = Get-Random
            Date = Get-Date
        }
    }
}

Measure-Command {
    for($x=0; $x -lt 2000; $x++)
    {
        [Ordered]@{
            ID = $x
            RandomNumber = Get-Random
            Date = Get-Date
        }
    }
}

如结果所示,最后两种方法的效率是 CSV 方法的大约三倍。在我们的测试系统上,所有的测试都在一秒之内完成,所以现实环境中影响并不大。

请挑选一个您自己最喜欢的方式——不过请注意最后一个例子需要 PowerShell 3.0 或更高的版本。

PowerShell 技能连载 - 通过关键词查找脚本

随着您硬盘上的 PowerShell 脚本数量的增多,要想找到您想要的脚本会变得越来越困难。以下是一个叫做 Find-Script 的工具函数。只要传入一个关键词,PowerShell 将会在您的个人文件夹下找出所有包含该关键词的脚本。

查找的结果将在一个 GridView 窗口中显示,您可以选中其中的文件,按下确认按钮以后将用 ISE 编辑器打开这些文件。

function Find-Script
{
    param
    (
        [Parameter(Mandatory=$true)]
        $Keyword,

        $Maximum = 20,
        $StartPath = $env:USERPROFILE
    )

    Get-ChildItem -Path $StartPath -Filter *.ps1 -Recurse -ErrorAction SilentlyContinue |
      Select-String -SimpleMatch -Pattern $Keyword -List |
      Select-Object -Property FileName, Path, Line -First $Maximum |
      Out-GridView -Title 'Select Script File' -PassThru |
      ForEach-Object { ise $_.Path }
}

默认情况下,Find-Script 只返回满足搜索条件的前 20 个脚本。您可以通过 -Maximum-StartPath 参数来改变最大搜索条数和搜索位置。

在PowerShell中操作Word - 使用格式化样式

在这个系列的上一步中,我们演示了用Windows PowerShell创建Microsoft Word文档的基本步骤。如果您试用了我的示例脚本,您会注意到文档格式化方面略有不足。幸运的是,我们有一些简洁的办法来改进您文档的质量,我将会在这篇文章中向您演示这个过程。我们将用第一部分的脚本作为起点。

关键之处在于 Selection 对象。

PS C:\>$word=new-object -ComObject "Word.Application"
PS C:\>$doc=$word.documents.Add()
PS C:\> $selection=$word.Selection

您可以更改 Selection 对象的一个重要元素是 Font。您可以轻松地修改字体大小和颜色,以及使用哪种字体。我将把日期和时间的字体改为绿色。

PS C:\> $selection.Font.Color="wdColorGreen"
PS C:\> $selection.TypeText((Get-Date))

在VBScript的年代中,我们需要定义 wdColorGreen 的值并将它赋给一个常量。但是在PowerShell中我们可以轻松地以字符串的形式插入这个常量。您一定很好奇有哪些颜色可以使用?问问PowerShell吧:

PS C:\> [enum]::GetNames([microsoft.office.interop.word.wdcolor])

您需要把字体颜色改回来,除非您需要把整个文档设为这个颜色。

PS C:\ >$selection.font.Color="wdColorAutomatic"
PS C:\> $selection.TypeParagraph()

在我原先的脚本中我插入了一个标题。现在我们把它变成大一点的字体。我将用我上次使用的WMI代码。

$selection.Font.Size=12
$selection.TypeText("Operating System Information for $($os.CSName)")

回顾一下前面一篇文章,有一个问题是PowerShell输出到Word早期是用等宽字体而后来用的是非等宽字体。解决方法是从PowerShell中插入结果之前指定一个合适的字体。

PS C:\> $selection.Font.Size=10
PS C:\> $selection.Font.Name="Consolas"
PS C:\> $selection.TypeText(($os | Select -Property $props | Out-String))

还要做的最后一件事是添加一段格式化的文本,说明报告的创建者。我希望采用Word的斜体格式来呈现。

PS C:\> $selection.Font.size=8
PS C:\> $selection.Font.Name="Calibri"
PS C:\> $selection.Font.Italic=$True
PS C:\> $by="Report created by $env:userdomain\$env:username"
PS C:\> $selection.TypeText($by)

我相信您一定也掌握了如何使文本变成粗体。

除了指定字体之外,您还可以采用Word内置的样式。

$selection.Style="Title"
$selection.TypeText("Operating System Report")
$selection.TypeParagraph()

您可以用PowerShell查询 Document 对象,看看有哪些样式可以用。

$doc.Styles | select NameLocal

大多数这些样式只能应用在文本的第一行,不过您也可以自己做实验调整一下。

通过这些步骤您可以简洁地通过您的PowerShell脚本创建一个漂亮的Word文档。请下载版本修订过的脚本,New-WordDoc2,并且自己做一下实验。

用PowerShell操作Office系列文章