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系列文章

PowerShell 技能连载 - 在文件管理器中显示隐藏文件

PowerShell可以方便地读写注册表。注册表是Windows设置的中心仓库。

这是一个可以设置文件管理器显示/不显示隐藏文件的函数。它聪明的地方在于不需要向注册表写入新值。它相当于文件管理器窗口如何显示和改变它们的内容。

function Show-HiddenFile
{
    param([Switch]$Off)

    $value = -not $Off.IsPresent
    Set-ItemProperty -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced `
    -Name Hidden -Value $value -type DWORD

    $shell = New-Object -ComObject Shell.Application
    $shell.Windows() |
        Where-Object { $_.document.url -eq $null } |
        ForEach-Object { $_.Refresh() }
}

Show-HiddenFile 使得隐藏文件变得可见而 Show-HiddenFile -Off 使得隐藏文件不可见。操作结果几乎在所有文件管理窗口中立即生效。如果您在没有打开文件管理窗口的情况下做出改变,则改变不会立即生效,因为没有可以调用 Refresh() 方法的窗口。

在PowerShell中操作Excel - 创建一个简单的报表

除了文本文件之外,Windows系统管理员最常用的工具是Microsoft Excel。我观察论坛中人们的讨论发现,有一大堆关于Excel电子表格讨论。许多人想要读写Excel的方法。通常,我告诉人们如果他们使用PowerShell,那么可以简单地将结果导出到CSV文件,然后用Excel打开,但是您很有可能需要一个真正的Excel文件。

我着手准备写几个关于如何在PowerShell中操作Excel的专题。今天我们将要通过Microsoft Excel和Windows PowerShell创建一个简单的报表。在第二部分中,我将演示如何创建一个更复杂的Excel文档。然后在第三部分中,我将向您演示如何从Excel文件中读取数据。

创建一个简单的报表

Microsoft Excel包含一个非常复杂的COM对象模型,我们可以在Windows Powershell中和它交互。让我们从头开始创建一个Excel应用程序的对象。

PS C:\> $xl=New-Object -ComObject "Excel.Application"

执行完这一步以后,Excel已经开始在后台运行,虽然看不见可交互窗口。

PS C:\> get-process excel

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    203      23    16392      24340   267     0.28   1280 EXCEL

下一步,我们将要创建一个工作簿对象。

PS C:\> $wb=$xl.Workbooks.Add()

下一步,我们将要创建一个工作表对象。

PS C:\> $ws=$wb.ActiveSheet

您可以将任何一个对象通过管道输出到 Get-Member 来学习它们。下一步,我们将使这个应用程序可见。

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

当您开始写脚本的时候,您可以不必做这步。但是这步能帮您检验我们写的PowerShell命令的执行结果。有很多种办法能将信息输入到电子表格中。做为一个简单的任务,我将演示如何使用单元格(cell)对象。

PS C:\> $cells=$ws.Cells

我们可以用行和列坐标来获取每一个单元格对象。

PS C:\> $cells.item(1,1)

如果您试这行代码,您将获取到很多信息。我们将继续往下并且输入一些信息到这个单元格。

PS C:\> $cells.item(1,1)=$env:computername

您的计算机名将会被填入 A1 单元格。让我们来填入更多的数据。

PS C:\> $cells.item(1,2)=$env:username
PS C:\> $cells.item(2,1)=(get-Date)

这个过程真的很简单。您只需要不断地记下当前的位置即可。如果您需要基本的格式,您可以使用每个单元格的 Font 属性。

PS C:\> $cells.item(1,1).font.bold=$True
PS C:\> $cells.item(1,2).font.bold=$True
PS C:\> $cells.item(1,1).font.size=16
PS C:\> $cells.item(1,2).font.size=16

好了,现在我们可以用 WorkBook 对象的 SaveAs() 方法保存这个文件。

PS C:\> $wb.SaveAs("c:\work\test.xlsx")

To fully exit, we’ll close the workbook and quit Excel.
若要完全退出,我们需要关闭工作簿并且退出Excel。

PS C:\> $wb.Close()
PS C:\> $xl.Quit()

如果您检查进程的话,您也许会发现Excel任然在运行,但它将会在5-10分钟之内退出,自少按我的经验是这样。以上是基本的要点,但在圆满完成之前,让我整理一个脚本,将这些材料整合在一起。

Param([string]$computer=$env:computername)

#get disk data
$disks=Get-WmiObject -Class Win32_LogicalDisk -ComputerName $computer -Filter "DriveType=3"

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

$wb=$xl.Workbooks.Add()
$ws=$wb.ActiveSheet

$cells=$ws.Cells

$cells.item(1,1)="{0} Disk Drive Report" -f $disks[0].SystemName
$cells.item(1,1).font.bold=$True
$cells.item(1,1).font.size=18

#define some variables to control navigation
$row=3
$col=1

#insert column headings
"Drive","SizeGB","FreespaceGB","UsedGB","%Free","%Used" | foreach {
    $cells.item($row,$col)=$_
    $cells.item($row,$col).font.bold=$True
    $col++
}

foreach ($drive in $disks) {
    $row++
    $col=1
    $cells.item($Row,$col)=$drive.DeviceID
    $col++
    $cells.item($Row,$col)=$drive.Size/1GB
    $cells.item($Row,$col).NumberFormat="0"
    $col++
    $cells.item($Row,$col)=$drive.Freespace/1GB
    $cells.item($Row,$col).NumberFormat="0.00"
    $col++
    $cells.item($Row,$col)=($drive.Size - $drive.Freespace)/1GB
    $cells.item($Row,$col).NumberFormat="0.00"
    $col++
    $cells.item($Row,$col)=($drive.Freespace/$drive.size)
    $cells.item($Row,$col).NumberFormat="0.00%"
    $col++
    $cells.item($Row,$col)=($drive.Size - $drive.Freespace) / $drive.size
    $cells.item($Row,$col).NumberFormat="0.00%"
}

$xl.Visible=$True

$filepath=Read-Host "Enter a path and filename to save the file"

if ($filepath) {
    $wb.SaveAs($filepath)
}

这也许是您想在PowerShell里做的事情:用WMI获取磁盘使用信息并将其记录在Excel电子表格中。这段脚本以计算机名做为参数,缺省值为localhost。然后使用Get-WMIObject来获取磁盘信息。

脚本的第一部分看起来应该很熟悉,它创建一个Excel应用程序和对象。该脚本向A1单元格插入一个标题。

$cells.item(1,1)="{0} Disk Drive Report" -f $disks[0].SystemName
$cells.item(1,1).font.bold=$True
$cells.item(1,1).font.size=18

脚本的主体部分从每个逻辑磁盘中提取数据,并且将一些属性写入Excel。由于我需要通过行和列来操作这些单元格对象,所以我将定义一些用来定位用的辅助变量。

$row=3
$col=1

通过它们,我可以插入我的表头。

"Drive","SizeGB","FreespaceGB","UsedGB","%Free","%Used" | foreach {
    $cells.item($row,$col)=$_
    $cells.item($row,$col).font.bold=$True
    $col++
}

每循环一次,$col就增加1,所以达到向右“移动”的效果。现在我需要遍历disks集合。每次需要“向下”移动一行,并且从第一列开始。

foreach ($drive in $disks) {
    $row++
    $col=1
    $cells.item($Row,$col)=$drive.DeviceID
    $col++
    $cells.item($Row,$col)=$drive.Size/1GB
    $cells.item($Row,$col).NumberFormat="0"
    $col++
...

接下来我将合适的WMI属性插入对应的单元格。每增加一行,我可以从左开始这个过程。请注意我使用了 NumberFormat 属性来格式化每个单元格的值。有一种探索的方法是创建一个Excel宏来记下所有您希望的步骤,然后查看生成的VBA代码。通过稍许的练习,您可以将这些命令翻译为PowerShell代码。

当脚本向电子表格写完数据以后,我把它显示出来并且提示用户输入文件名。如果用户输入的文件名,那么文件以该文件名保存。否则,您可以继续编辑电子表格,然后手动保存它。这个演示脚本并不会自动关闭Excel。这个脚本执行的结果如图1所示:

PowerShell生成的Excel报表

结论

我知道还有很多问题,所以我将会把它们总结出来。在第二部分,我们将看到一些高级的格式化选项,以及其它使用Microsoft Excel的深入用法。如果您将要用PowerShell来创建Excel文档,您会尽可能做到极致。

用PowerShell操作Office系列文章