PowerShell 技能连载 - 持续监视脚本的运行
以下是一段演示如何在 Windows 注册表中存储私人信息的代码:
1 | # store settings here |
当运行这段代码时,它将返回一个对象,该对象告诉您上次运行此脚本是什么时候,以及从那以后运行了多长时间。
以下是一段演示如何在 Windows 注册表中存储私人信息的代码:
1 | # store settings here |
当运行这段代码时,它将返回一个对象,该对象告诉您上次运行此脚本是什么时候,以及从那以后运行了多长时间。
If you use Outlook to organize your calendar events, here is a useful PowerShell function that connects to Outlook and dumps your calendar entries:
Function Get-OutlookCalendar
{
# load the required .NET types
Add-Type -AssemblyName 'Microsoft.Office.Interop.Outlook'
# access Outlook object model
$outlook = New-Object -ComObject outlook.application
# connect to the appropriate location
$namespace = $outlook.GetNameSpace('MAPI')
$Calendar = [Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderCalendar
$folder = $namespace.getDefaultFolder($Calendar)
# get calendar items
$folder.items |
Select-Object -Property Start, Categories, Subject, IsRecurring, Organizer
}
Try this:
PS> Get-OutlookCalendar | Out-GridView
How would you query for all AD users with names that start with a “e”-“g”? You shouldn’t use a client-side filter such as Where-Object. One thing you can do is use the -Filter parameter with logical operators such as -and and -or:
Get-ADUser -filter {(name -lt 'E') -or (name -gt 'G')} |
Select-Object -ExpandProperty Name
this example requires the free RSAT tools from Microsoft to be installed)
Maybe you’d like to add a column with incrementing indices to your objects. Try this:
$startcount = 0
Get-Service |
Select-Object -Property @{N='ID#';E={$script:startcount++;$startcount}}, * |
Out-GridView
When you run this chunk of code, you get a list of services in a grid view window, and the first column “ID#” is added with incrementing ID numbers.
The technique can be used to add arbitrary columns. Simply use a hash table with key N[ame] for the column name, and key E[xpression] with the script block that generates the column content.
在每一个技能中我们解释了 Group-Object
能为您做什么,以及它有多么好用。不幸的是,Group-Object
的性能不理想。如果您试图对大量对象分组,该 cmdlet 可能会消耗大量时间。
以下是一行按文件大小对您的用户文件夹中所有文件排序的代码。当您希望检测重复的文件时,这将是一个十分重要的先决条件。由于这行代码将在最终返回结果,所以将会消耗大量的时间,甚至数小时:
1 | $start = Get-Date |
由于这些限制,我们创建了一个基于 PowerShell 的 Group-Object
的实现,并称它为 Group-ObjectFast
。它基本上做相同的事请,只是速度更快。
1 | function Group-ObjectFast |
只需要将上述例子中的 Group-Object
替换为 Group-ObjectFast
,就可以体验它的速度:
1 | $start = Get-Date |
在我们的测试中,Group-ObjectFast
比 Group-Object
快了大约 10 倍。
Group-Object
是一个好用的 cmdlet:它可以方便地可视化分组。请查看以下示例:
1 | Get-Process | Group-Object -Property Company |
Basically, the cmdlet builds groups based on the content of a given property. You can also omit the group, and just look at the count if all that matters to you are the number distributions:
基本上,该 cmdlet 基于指定的属性内容创建分组。当您只关注数量分布时,也可以忽略该分组,而只查看总数。
1 | PS C:\> Get-ChildItem -Path c:\windows -File | Group-Object -Property Extension -NoElement | Sort-Object -Property Count -Descending |
如果您对实际对象的分组更感兴趣,可以通过 Group-Object
来返回一个哈希表。通过这种方式,您可以通过他们的键访问每个特定的分组:
1 | $hash = Get-ChildItem -Path c:\windows -File | |
执行的结果将转储 Windows 目录下所有扩展名为 “.exe” 的文件。请注意键(查询的属性)不为字符串的情况。
类似地,如果您使用 PowerShell 远程操作并且扇出到多台计算机来平行地获取信息,当获取到结果时,Group-Object
将会把结果重新分组。这个示例同时从三台机器获取服务信息,而且结果将以随机顺序返回。Group-Object
将会对输入的数据分组,这样您可以操作计算机的结果。和哈希表的操作方法一样,您可以用方括号或点号来存取哈希表的键:
1 | $services = Invoke-Command -ScriptBlock { Get-Service } -ComputerName server1, server2, server3 | Group-Object -Property PSComputerName -AsHashTable |
以下是一个类似的示例,但有一个 bug。您能指出错误吗?
1 | $hash = Get-Service | |
当您查看哈希表时,您可能希望获取正在运行的服务:
1 | PS C:\> $hash |
When you look at the hash table, you would expect to get back the running services:
然而,您并不会获得任何结果。那是因为哈希表中的那个键并不是字符串而是一个 “ServiceControllerStatus” 对象:
1 | PS C:\> $hash.Keys | Get-Member |
要确保获得到的是可存取的键,请总是将 -AsHashTable
和 -AsString
合并使用。后者确保把键转换为字符串。现在示例代码可以按预期工作:
1 | $hash = Get-Service | |
有些时候,需要自动化操作某些已经人工打开的网站。也许您需要先用 WEB 表单登录到内部的网页。假设网站是通过 Internet Explorer 加载的(不是 Edge 或任何第三方浏览器),您可以使用 COM 接口来访问浏览器的实时内容。
当您访问动态网页时,纯 HTML 元素可能会更有用。一个纯 WebClient
(或是 Invoke-WebRequest
cmdlet)只会返回静态 HTML,并不是用户在浏览器中看到的内容。当使用一个真实的浏览器显示网页内容时,您的脚本需要访问驱动显示内容的完整 HTML。
要测试这一点,请打开 Internet Explorer 或者 Edge,并浏览到需要的网站。在我们的例子中,我们导航到 www.powershellmagazine.com。
1 | $obj = New-Object -ComObject Shell.Application |
在 $browser
中,您可以访问打开的浏览器中的对象模型。如果 $browser
为空,请确保您调整了 LocationUrl
的过滤条件。不要忘了两端的星号。
如果您希望挖掘网页中的所有图片,以下是获取所有图片列表的方法:
1 | $browser.Document.images | Out-GridView |
类似地,如果您希望挖掘网页的内容信息,以下代码返回页面的 HTML:
1 | PS> $browser.Document.building.innerHTML |
您可以使用正则表达式来挖掘内容。不过有一个限制:如果您需要以已登录的 WEB 用户的上下文来进行额外的操作,那么别指望了。例如,如果您需要下载一个需要登录才能获取的文件,那么您需要通过对象模型调用 Internet Explorer 的下载操作。
您可能无法通过 Invoke-WebRequest 或是其它简单的 WEB 客户端来下载文件,因为 PowerShell 运行在它自己的上下文中。而对于网站而言,看到的是一个匿名访问者。
使用 Internet Explorer 对象模型来进行更多高级操作,例如下载文件或视频,并不是完全不可行。但基本上是十分复杂的,您需要向用户界面发送点击和按键动作。
从 Windows 8 和 Server 2012 R2 起,这些操作系统附带发行了一个名为 PrintManagement
的 PowerShell 模块。该模块中的 cmdlet 可以实现脚本化安装和配置打印机。以下是一段帮助您起步的代码:
1 | $PrinterName = "MyPrint" |
它从驱动库中安装了一个新的打印机。新的打印机默认没有在网络上共享,因为需要管理员权限。作为管理员,您可以运行 Set-Printer
来启用共享,也可以向 Add-Printer
命令添加 -Shared
开关参数。
要探索 PrintManagement
模块中的其它 cmdlet,请使用这行代码:
1 | PS> Get-Command -Module PrintManagement |
请注意 Windows 7 中没有包含该模块,而且无法在 Windows 7 中安装,因为 Windows 7 缺少运行该模块中 cmdlet 的某些依赖项。
有些时候通过简单的基于文本的 CSV 格式来批量创建对象是一种聪明的方法,尤其是原始数据已是基于文本的而且只需要少量重格式化。
以下是一个简单的例子,以这种方式输入信息并创建一个自定义对象的列表:
1 | $text = 'Name,FirstName,Location |
结果看起来类似这样:
1 | Name FirstName Location |
经常地,AD 管理员需要查找某个 AD 组的所有成员,包括嵌套的成员。以下是一个常常出现在示例中的代码片段,用于解决这个问题:
1 | $groupname = 'External_Consultants' |
(请注意您需要来自 Microsoft 免费的 RSAT 工具来使用这些示例中的 cmdlet。)
当您将 $groupname
中的组名改为您组织中存在的 AD 组名后,该代码不仅返回组中的直接用户,而且包含既在该组又在其它组中的直接用户。
然而,该代码执行起来非常慢。以下是一个更简单的实现,能达到多于五倍的速度:
1 | $groupname = 'External_Consultants' |
它的内部使用合适的 LDAP 过滤器,和以上直接的方法类似:
1 | $groupname = 'External_Consultants' |