Visual Studio的PowerShell扩展工具

Visual Studio的PowerShell扩展工具为Visual Studio 2012和2013 RC增加了PowerShell语言支持。

本工具目前为BETA版。

下载地址:Visual Studio工具库
开源项目:GitHub

必须条件:

编辑器扩展

语法高亮、智能感知和代码折叠

Syntax highlighting, IntelliSense and code folding

方法导航

Function navigation

调试器扩展

脚本输出

Script Output

断点支持

Breakpoint Support

本地变量支持

Locals Support

调用堆栈支持

Stack Frame Support

工程系统支持

Project System Support

在PowerShell中操作Word - 生成文档

我假设许多读者用PowerShell生成服务器、事件以及Windows网络上各种数据的报表。您也许创建过纯文本文件,甚至漂亮的HTML报表。但是您也可以用上Microsoft Word,当然,前提是您已经安装了Word。在这个系列的第二部分,我将会向您演示如何用PowerShell为Word文档套用样式

创建Word应用程序对象

PowerShell可以通过COM接口控制Word应用程序。有趣的地方是,虽然您可以交互式地做所有的操作,但我希望您最终能够用脚本操作一切。我们从创建一个Word程序对象开始。

PS C:\> $word=new-object -ComObject "Word.Application"

如果您好奇的话,可以将这个对象输出到 Get-Member 命令。我们将创建一系列对象,您可以将每一个对象通过管道输出到 Get-Member 来探索它们。

下一步,我们创建一个文档对象。

PS C:\> $doc=$word.documents.Add()

现在,Word程序已经启动,并且创建了一个新文档,但是您在屏幕上看不到任何东西。通常这是正常的,因为我们需要它在后台运行。但是如果您想查看所创建的文档,您需要将应用程序的 Visible 属性设置为 True

PS C:\> $word.Visible=$True

在我们插入文本之前,我们需要获取焦点。创建一个 Selection 对象可以帮我们做一些诸如设置字体大小和颜色等操作,我们将在第二部分介绍这些操作。

PS C:\> $selection=$word.Selection

用PowerShell在文档中插入文本

现在光标在文档的顶部,现在可以开始插入文本了。我们将用 Selection 对象的 TypeText() 方法插入当前的日期和时间。

PS C:\> $selection.TypeText((Get-Date))

如果我们继续插入文本,那么文本将会紧挨在日期的后面。现在我们用 TypeParagraph() 方法插入一个回车符。

PS C:\> $selection.TypeParagraph()

让我们继续插入一些文本。我将用WMI获取本地计算机的操作系统信息。

PS C:\> $os=Get-WmiObject -class win32_OperatingSystem
PS C:\> $selection.TypeText("Operating System Information for $($os.CSName)")

由于我希望写入所有的非系统属性,所以我将快速递创建一个数组用来保存所有的属性名。

PS C:\> $os.properties | select Name | foreach -begin {$props=@()} -proc {$props+="$($_.name)"}

现在我可以从 $os 获取所有的属性并插入Word文档。很重要的一点是 TypeText() 的值是字符串型的,所以我需要将内联的PowerShell表达式通过管道输出到 Out-String

PS C:\> $selection.TypeText(($os | Select -Property $props | Out-String))

如果需要的话,还可以继续插入文字和图片。当完成操作以后,我将保存并关闭文档。

PS C:\> $doc.SaveAs([ref]"c:\work\osreport.docx")
PS C:\> $doc.Close()

请确认使用 [ref] 为文件路径转换数据类型。假设我不再创建新的文档,那么剩下的就是关闭Word应用程序。

PS C:\> $word.quit()

这些就是要做的所有事情。最终生成的Word文档是可用的,虽然可能不太漂亮。在我的例子中发现一个问题:Word用的事非等宽字体,而PowerShell的输出格式假设用的是等宽字体。(译者注:可能会造成输出的结果对不整齐)。在第二部分,我将向您演示如何解决这些问题。同时,欢迎下载示例脚本 New-WordDoc.ps1

用PowerShell操作Office系列文章

PowerShell 技能连载 - 设置显示器亮度

如果您的显示驱动程序支持WMI,那么您可以用PowerShell改变显示器的亮度——甚至是远程的计算机!

以下是实现改变显示器亮度的函数:

function Set-MonitorBrightness
{
    param
    (
        [Parameter(Mandatory=$true)]
        [Int][ValidateRange(0,100)]
        $Value,

        $ComputerName,
        $Credential
    )

    $null = $PSBoundParameters.Remove('Value')

    $helper = Get-WmiObject -Namespace root/WMI -Class WmiMonitorBrightnessMethods @PSBoundParameters
    $helper.WmiSetBrightness(1, $Value)
}

只需要指定一个0-100之间的值,您就可以看到显示器亮度发生改变。为 -ComputerName 参数指定远程计算机名或IP地址(均支持多个),然后您远程的同事们会惊讶地发现去吃午餐的时候显示器都变暗了!当然,远程操作WMI需要本地管理员权限,并且为防火墙设置了允许远程管理的规则。

如果提示“不支持”的错误提示信息,那么说明您的显示驱动程序不支持WMI。

这是“有趣”的部分:模拟一个古怪的显示效果:

for($x=0; $x -lt 20; $x++)
{
    Set-MonitorBrightness -Value (Get-Random -Minimum 20 -Maximum 101)
    Start-Sleep -Seconds 1
}

PowerShell 技能连载 - 检测显示器亮度

如果您想检查您当前的显示器亮度(当然,尤其是针对笔记本电脑),以下是一个快捷的函数:

function Get-MonitorBrightness
{
    param($ComputerName, $Credential)

    Get-WmiObject -Namespace root/WMI -Class WmiMonitorBrightness @PSBoundParameters |
        Select-Object -Property PSComputerName, CurrentBrightness, Levels
}

它甚至支持 -ComputerName-Credential,所以您也可以查询远程的主机。

如果您为 -ComputerName 参数传入一个用逗号分隔的主机名或IP地址列表,您将获得所有具有local Admin权限的主机的执行结果。

PowerShell 技能连载 - 创建符号链接

符号链接使用起来很像“普通”的链接文件(*.lnk):它们可以虚拟地指向任何文件或者文件夹,甚至UNC路径。和lnk文件不同的是,创建符号链接需要完整管理员权限,并且用户不可以存取符号链接属性。

以下是一个创建符号链接的函数:

function New-SymbolicLink
{
    param
    (
        [Parameter(Mandatory=$true)]
        $OriginalPath,

        [Parameter(Mandatory=$true)]
        $MirroredPath,

        [ValidateSet('File', 'Directory')]
        $Type='File'
    )

    if(!([bool]((whoami /groups) -match "S-1-16-12288") ))
    {
        Write-Warning 'Must be an admin'
        break
    }
    $signature = '
        [DllImport("kernel32.dll")]
        public static extern bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, int dwFlags);
        '
    Add-Type -MemberDefinition $signature -Name Creator -Namespace SymbolicLink

    $Flags = [Int]($Type -eq 'Directory')
    [SymbolicLink.Creator]::CreateSymbolicLink($MirroredPath, $OriginalPath,$Flags)
}

$downloads = "$env:userprofile\Downloads"
$desktop = "$env:userprofile\Desktop\MyDownloads"

New-SymbolicLink -OriginalPath $downloads -MirroredPath $desktop -Type Directory

当您(以管理员身份)运行这段代码时,它将使您能在桌面上访问下载文件夹。请右击符号链接并选择属性,并和“普通”的*.link文件做对比。

PowerShell 技能连载 - 检查管理员权限

以下通过一个非常规的办法实现检查一段脚本是否以管理员权限运行(通过提升UAC),这体现了PowerShell强大的灵活性:

function Test-Admin { [bool]((whoami /groups) -match "S-1-16-12288") }

它的基本原理是检查当前用户是否是高完整性级别用户组的成员。该用户组是专门针对提升权限的管理员设置的。

如果您不想使用本地命令(whoami.exe)的话,还可以使用更贴近PowerShell(或.NET)的方法,如以下代码所示:

function Test-Admin {

    $id = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())

    $id.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)

}

PowerShell 技能连载 - 创建硬连接

硬连接是NTFS文件系统中文件的“镜像”。它们使得一个文件能在多个文件系统位置(必须在一个卷中)中出现。

所以文件仅仅占用它的原始位置空间,但是在其它地方也可用。当您需要让大文件在多个地方可用的时候,这是一个十分有用的功能。

以下是 New-HardLink 函数的介绍。它演示了PowerShell如何调用底层的API函数:

function New-HardLink
{
    param
    (
        [Parameter(Mandatory=$true)]
        $OriginalFilePath,

        [Parameter(Mandatory=$true)]
        $MirroredFilePath
    )

    $signature = '
            [DllImport("Kernel32.dll")]
            public static extern bool CreateHardLink(string lpFileName,string lpExistingFileName,IntPtr lpSecurityAttributes);
    '
    Add-Type -MemberDefinition $signature -Name Creator -Namespace Link

    [Link.Creator]::CreateHardLink($MirroredFilePath,$OriginalFilePath,[IntPtr]::Zero)

}

以下是它的使用方法:

$Original = "$env:temp\testfile.txt"
$Copy1 = "$env:userprofile\Desktop\mirrorfile1.txt"
$Copy2 = "$env:userprofile\Desktop\mirrorfile2.txt"

# create original file:
Set-Content -Path $Original -Value 'Hello'

# create hard link #1:
New-HardLink -OriginalFilePath $Original -MirroredFilePath $Copy1

# create hard link #2:
New-HardLink -OriginalFilePath $Original -MirroredFilePath $Copy2

这段代码首先在临时文件夹中创建一个物理文件。然后在您的桌面上创建两个硬连接。它们看上去分别是mirrorfile1.txtmirrorfile2.txt。虽然它们看上去像是独立的文件,而实际上他们都指向刚创建的临时文件。

您可以打开桌面上两个文件中的某一个,做一些修改,然后保存并关闭。当打开另一个文件时,您可以看到一模一样的修改后的内容。您还可以简单地删掉一个镜像文件来移除硬连接。

用 PowerShell 处理纯文本 - 1

原始文本:”data1”:111,”data2”:22,”data3”:3,”data4”:4444444,”data5”:589
要求:转换成对象

方法一,采用字符串运算及 ConvertFrom-StringData 命令:

$rawTxt='"data1":111,"data2":22,"data3":3,"data4":4444444'
$rawTxt -split ',' | ForEach-Object {
   $temp= $_ -split ':'
   "{0}={1}" -f $temp[0].Substring(1,$temp[0].Length-2),$temp[1]
} | ConvertFrom-StringData

方法二,采用正则表达式,使用.NET的方法:

$rawTxt = '"data1":111,"data2":22,"data3":3,"data4":4444444,"data5":589'
$regex = [regex] '"(?<name>\w*)":(?<value>\d*),?'
$match = $regex.Match($rawTxt)
while ($match.Success) {
    [PSCustomObject]@{
        Name = $match.Groups['name'].Value
        Value = $match.Groups['value'].Value
    }
    $match = $match.NextMatch()
}

方法三,采用正则表达式,使用 Select-String Cmdlet:

Select-String -InputObject $rawTxt -Pattern $regex -AllMatches | % {
    $_.Matches
} | % {
   [PSCustomObject]@{
        Name = $_.Groups['name'].Value
        Value = $_.Groups['value'].Value
    }
}

三者的执行结果都是这样:

Name          Value
----          -----
data1         111
data2         22
data3         3
data4         4444444
data5         589

原命题参见:[PowerShell 文本处理实例(三)] 1

获取2013年剩余天数

用PowerShell获取2013年剩余天数的两种写法。

((Get-Date 2014-1-1) - (Get-Date)).Days
100

([datetime]"2014-1-1" - [datetime]::now).Days
100

顺便励志一下,2013年只剩下100天了。Come on,小伙伴们!

在PowerShell中操作Excel - 读取数据

欢迎回到“在PowerShell中操作Excel”三部曲系列文章。在这一系列的前两部分,我们学习了如何将数据写入Excel并且创建“富”报表,以及额外的格式化选项等Microsoft Excel高级用法。

对于IT专家来说,这个故事的另一半是如何从一个Excel文档中读取数据。它的挑战性在于您必须事先知道工作表的结构。我们可以搜索数据,但那是更复杂的情况。我假设您已有一个用过且了其解结构的Excel文档。这样,用PowerShell读取数据就不那么复杂。

读取数据

像我们在本系列文章的前两部分那样,第一步是创建一个Excel应用程序对象。

$xl=New-Object -ComObject "Excel.Application"

我将在我的脚本中使用这个Excel文件。

Excel数据

用工作簿对象的 Open() 方法打开文件。

$wb=$xl.Workbooks.Open($file)
$ws=$wb.ActiveSheet

$ws对象是我们对数据最重要的的引用点。我需要用的数据从A2单元格开始。在我的测试环境中,我也许知道我需要处理多少行,但是既然我知道从哪儿开始,我可以用一个Do循环来读取每一行,获取数据,进行进一步操作。

$Row=2

do {
  $data=$ws.Range("A$Row").Text
...

通过使用Range属性,我可以获取A2单元格。Text属性是该单元格的值。我的示例脚本将要从第一列获取计算机名,获取一些WMI信息,然后向管道写入一个和电子表格的其它部分数据有关的自定义数据。

当您处理Excel数据的时候,我建议您进行一系列校验。假设单元格里有一个数据,我假设它是一个机器名,那么我会试着ping一下它。

if ($data) {
    Write-Verbose "Querying $data"
      $ping=Test-Connection -ComputerName $data -Quiet

如果ping通了,我将会使用WMI来获取操作系统名称,否则我会设置$OS变量为$Null。

if ($Ping) {
        $OS=(Get-WmiObject -Class Win32_OperatingSystem -Property Caption -computer $data).Caption
      }
      else {
        $OS=$Null

最后,对于每台计算机,我将用 New-Object cmdlet创建一个自定义对象。

New-Object -TypeName PSObject -Property @{
        Computername=$Data.ToUpper()
        OS=$OS
        Ping=$Ping
        Location=$ws.Range("B$Row").Text
        AssetAge=((Get-Date)-($ws.Range("D$Row").Text -as [datetime])).TotalDays -as [int]
      }

请注意我设置的其它属性值,比如说Location,是位于B2单元格,至少对于这台计算机而言。请注意您从它的Text属性获取到的只是文本。但您还可以将它们转换为各种数据类型,就像我对AssetAge属性的处理那样。我从D2单元格读取文本,并把它转换为一个 DateTime 对象,于是我可以将它和当前时间做减法,得到一个 TimeSpan 对象。该对象有一个 TotalDays 属性。

loop循环的最后一步是使行计数器自增1。

$Row++
} While ($data)

下一次进入lopp循环的时候,脚本将会处理第3行的数据。直到PowerShell遇到一个空行。在最后,我将关闭文件并且退出。

$xl.displayAlerts=$False
$wb.Close()
$xl.Application.Quit()

我的脚本运行以后生成一下输出结果:

PS C:\scripts> .\Demo-ReadExcel.ps1

AssetAge     : 687
Ping         : True
Computername : SERENITY
Location     : R1-1
OS           : Microsoft Windows 7 Ultimate

AssetAge     : 293
Ping         : True
Computername : QUARK
Location     : R1-4
OS           : Microsoft Windows 7 Professional

AssetAge     : 293
Ping         : False
Computername : SERVER01
Location     : R3-2
OS           :

AssetAge     : 2005
Ping         : True
Computername : JDHIT-DC01
Location     : R2-1
OS           : Microsoft(R) Windows(R) Server 2003, Enterprise Edition

我在PowerShell中用数的代码就可以实现从Excel电子表格中读取数据并且在我的程序中使用它。如果需要的话,我可以结合前面文章中的技术,在读取的同时更新电子表格的内容!

结论

您可以下载我的示例脚本并且自己做一下实验。记住,当使用PowerShell读取Excel文件的时候,您需要事先了解文档的结构,并且做好错误处理和数据有效性验证。我并不推荐初学者用PowerShell操作Excel,但具有一些经验并投入一些耐心以后,您可以得到很丰厚的回报。

用PowerShell操作Office系列文章