PowerShell 技能连载 - 理解类型加速器(第 2 部分)

PowerShell 带来了一系列硬编码的类型加速器,它们的效果就像通常使用的 .NET 类型,而且由于它们比原始数据类型名称短很多,所以它们“加快了打字速度”。

一个很少人知道的事实是类型加速器列表是可以扩展的。以下代码添加一个新的名为 “SuperArray” 的类型加速器,它指向 “System.Collections.ArrayList”。

您现在可以创建一个新的“超级数组”(它用起来像普通的数组,但是拥有一系列额外的方法来向指定的位置增删元素,而且附加数组元素也比普通的数组快得多):

1
2
3
[PSObject].Assembly.GetType('System.Management.Automation.TypeAccelerators')::Add('SuperArray', [System.Collections.ArrayList])

$a = [superarray]::new()

您还可以将一个普通数组转换成“超级数组”:

1
2
3
4
5
6
7
PS> $a = [superarray](1,2,3)

PS> $a.RemoveAt(1)

PS> $a
1
3

请注意虽然不用类型加速器也可以完成这项任务。但是得敲这么长的代码:

1
2
3
4
5
6
7
PS> $a = [System.Collections.ArrayList](1,2,3)

PS> $a.RemoveAt(1)

PS> $a
1
3

PowerShell 技能连载 - 理解类型加速器(第 1 部分)

“类型加速器”类似 .NET 类型别名。它们的目的是节约打字。例如,[ADSI] “类型”实际上并不存在。它只不过是 System.DirectoryServices.DirectoryEntry 的别名。您可以将 [ADSI] 替换为 [System.DirectoryServices.DirectoryEntry]

1
2
3
4
5
6
PS> [ADSI].FullName
System.DirectoryServices.DirectoryEntry


PS> [System.DirectoryServices.DirectoryEntry].FullName
System.DirectoryServices.DirectoryEntry

由于类型加速器是硬编码进 PowerShell 中的,所以使用它们是安全的。以下这行代码将显示所有预定义的类型加速器,如果您想使用 .NET 类型,您可以选用列表中的内容,因为它们在 PowerShell 中都很重要:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
PS> [PSObject].Assembly.GetType('System.Management.Automation.TypeAccelerators')::Get

Key Value
--- -----
Alias System.Management.Automation.AliasAttribute
AllowEmptyCollection System.Management.Automation.AllowEmptyCollecti...
AllowEmptyString System.Management.Automation.AllowEmptyStringAt...
AllowNull System.Management.Automation.AllowNullAttribute
ArgumentCompleter System.Management.Automation.ArgumentCompleterA...
array System.Array
bool System.Boolean
byte System.Byte
char System.Char
CmdletBinding System.Management.Automation.CmdletBindingAttri...
datetime System.DateTime
decimal System.Decimal
double System.Double
DscResource System.Management.Automation.DscResourceAttribute
float System.Single
single System.Single
guid System.Guid
hashtable System.Collections.Hashtable
int System.Int32
int32 System.Int32
int16 System.Int16
long System.Int64
int64 System.Int64
ciminstance Microsoft.Management.Infrastructure.CimInstance
cimclass Microsoft.Management.Infrastructure.CimClass
cimtype Microsoft.Management.Infrastructure.CimType
cimconverter Microsoft.Management.Infrastructure.CimConverter
IPEndpoint System.Net.IPEndPoint
NullString System.Management.Automation.Language.NullString
OutputType System.Management.Automation.OutputTypeAttribute
ObjectSecurity System.Security.AccessControl.ObjectSecurity
Parameter System.Management.Automation.ParameterAttribute
PhysicalAddress System.Net.NetworkInformation.PhysicalAddress
pscredential System.Management.Automation.PSCredential
PSDefaultValue System.Management.Automation.PSDefaultValueAttr...
pslistmodifier System.Management.Automation.PSListModifier
psobject System.Management.Automation.PSObject
pscustomobject System.Management.Automation.PSObject
psprimitivedictionary System.Management.Automation.PSPrimitiveDictionary
ref System.Management.Automation.PSReference
PSTypeNameAttribute System.Management.Automation.PSTypeNameAttribute
regex System.Text.RegularExpressions.Regex
DscProperty System.Management.Automation.DscPropertyAttribute
sbyte System.SByte
string System.String
SupportsWildcards System.Management.Automation.SupportsWildcardsA...
switch System.Management.Automation.SwitchParameter
cultureinfo System.Globalization.CultureInfo
bigint System.Numerics.BigInteger
securestring System.Security.SecureString
timespan System.TimeSpan
uint16 System.UInt16
uint32 System.UInt32
uint64 System.UInt64
uri System.Uri
ValidateCount System.Management.Automation.ValidateCountAttri...
ValidateDrive System.Management.Automation.ValidateDriveAttri...
ValidateLength System.Management.Automation.ValidateLengthAttr...
ValidateNotNull System.Management.Automation.ValidateNotNullAtt...
ValidateNotNullOrEmpty System.Management.Automation.ValidateNotNullOrE...
ValidatePattern System.Management.Automation.ValidatePatternAtt...
ValidateRange System.Management.Automation.ValidateRangeAttri...
ValidateScript System.Management.Automation.ValidateScriptAttr...
ValidateSet System.Management.Automation.ValidateSetAttribute
ValidateTrustedData System.Management.Automation.ValidateTrustedDat...
ValidateUserDrive System.Management.Automation.ValidateUserDriveA...
version System.Version
void System.Void
ipaddress System.Net.IPAddress
DscLocalConfigurationManager System.Management.Automation.DscLocalConfigurat...
WildcardPattern System.Management.Automation.WildcardPattern
X509Certificate System.Security.Cryptography.X509Certificates.X...
X500DistinguishedName System.Security.Cryptography.X509Certificates.X...
xml System.Xml.XmlDocument
CimSession Microsoft.Management.Infrastructure.CimSession
adsi System.DirectoryServices.DirectoryEntry
adsisearcher System.DirectoryServices.DirectorySearcher
wmiclass System.Management.ManagementClass
wmi System.Management.ManagementObject
wmisearcher System.Management.ManagementObjectSearcher
mailaddress System.Net.Mail.MailAddress
scriptblock System.Management.Automation.ScriptBlock
psvariable System.Management.Automation.PSVariable
type System.Type
psmoduleinfo System.Management.Automation.PSModuleInfo
powershell System.Management.Automation.PowerShell
runspacefactory System.Management.Automation.Runspaces.Runspace...
runspace System.Management.Automation.Runspaces.Runspace
initialsessionstate System.Management.Automation.Runspaces.InitialS...
psscriptmethod System.Management.Automation.PSScriptMethod
psscriptproperty System.Management.Automation.PSScriptProperty
psnoteproperty System.Management.Automation.PSNoteProperty
psaliasproperty System.Management.Automation.PSAliasProperty
psvariableproperty System.Management.Automation.PSVariableProperty

PowerShell 技能连载 - Out-Notepad: Send Information to Notepad

您是否曾希望将文本直接发送到记事本,而并不将它保存到文件中?

通常,您需要将文本保存到一个文件,然后通知它读取文件。还有一个更特别的方法:通过 Windows 消息和记事本通信,将文字发送到记事本。这是 Out-Notepad 函数的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#requires -Version 2
function Out-Notepad
{
param
(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
[String]
[AllowEmptyString()]
$Text
)

begin
{
$sb = New-Object System.Text.StringBuilder
}

process
{
$null = $sb.AppendLine($Text)
}
end
{
$text = $sb.ToString()

$process = Start-Process notepad -PassThru
$null = $process.WaitForInputIdle()


$sig = '
[DllImport("user32.dll", EntryPoint = "FindWindowEx")]public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("User32.dll")]public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
'

$type = Add-Type -MemberDefinition $sig -Name APISendMessage -PassThru
$hwnd = $process.MainWindowHandle
[IntPtr]$child = $type::FindWindowEx($hwnd, [IntPtr]::Zero, "Edit", $null)
$null = $type::SendMessage($child, 0x000C, 0, $text)
}
}

这是它的使用方法:

1
2
3
PS> Get-Service | Out-Notepad

PS> Get-Service | Out-String | Out-Notepad

这两行代码,都能打开一个全新的 Notepad 实例,所有服务信息都写入 Notepad。请注意它们的不同:第一行代码创建一个对象名称的列表。如果您希望对象的显示像在 PowerShell 中那样详细,请确保将他们通过管道送给 Notepad 之前先将它们用管道送给 Out-String

PowerShell 技能连载 - 使用秘密的 $FormatEnumerationLimit 变量

Format-List 缺省以列表的形式显示对象的属性,如果某个属性包含一个数组,该数组将会转换为文本,而且只显示一小部分数组元素。以下是一个例子:

1
2
3
4
5
6
7
PS> Get-Process -Id $Pid | Format-List -Property Name, Modules


Name : powershell_ise
Modules : {System.Diagnostics.ProcessModule (PowerShell_ISE.exe),
System.Diagnostics.ProcessModule (ntdll.dll), System.Diagnostics.ProcessModule
(MSCOREE.DLL), System.Diagnostics.ProcessModule (KERNEL32.dll)...}

这行代码获取 PowerShell 的进程,并且显示它的名称和加载的模块。如您所见,输出的结果并没有显示所有加载的模块。

有一个名为 FormatEnumerationLimit 的神秘变量,控制 Format-List 显示多少个数组元素。

缺省情况下,显示个数限制为 4 个,所以输出结果中最多显示 4 个数组元素。如果将限制值设为 -1,事实上相当于关闭了该限制:

1
2
3
4
5
6
7
PS> $FormatEnumerationLimit
4

PS> $FormatEnumerationLimit = -1

PS> $FormatEnumerationLimit
-1

如果您再次运行相同的命令,Format-List 将显示所有数组元素:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
PS> Get-Process -Id $Pid | Format-List -Property Name, Modules


Name : powershell_ise
Modules : {System.Diagnostics.ProcessModule (PowerShell_ISE.exe),
System.Diagnostics.ProcessModule (ntdll.dll), System.Diagnostics.ProcessModule
(MSCOREE.DLL), System.Diagnostics.ProcessModule (KERNEL32.dll),
System.Diagnostics.ProcessModule (KERNELBASE.dll), System.Diagnostics.ProcessModule
(ADVAPI32.dll), System.Diagnostics.ProcessModule (msvcrt.dll),
System.Diagnostics.ProcessModule (sechost.dll), System.Diagnostics.ProcessModule
(RPCRT4.dll), System.Diagnostics.ProcessModule (mscoreei.dll),
System.Diagnostics.ProcessModule (SHLWAPI.dll), System.Diagnostics.ProcessModule
(combase.dll), System.Diagnostics.ProcessModule (ucrtbase.dll),
System.Diagnostics.ProcessModule (bcryptPrimitives.dll),
System.Diagnostics.ProcessModule (GDI32.dll), System.Diagnostics.ProcessModule
(gdi32full.dll), System.Diagnostics.ProcessModule (msvcp_win.dll),
System.Diagnostics.ProcessModule (USER32.dll), System.Diagnostics.ProcessModule
(win32u.dll), System.Diagnostics.ProcessModule (IMM32.DLL),
System.Diagnostics.ProcessModule (kernel.appcore.dll), System.Diagnostics.ProcessModule
(VERSION.dll), System.Diagnostics.ProcessModule (clr.dll),
System.Diagnostics.ProcessModule (MSVCR120_CLR0400.dll),
System.Diagnostics.ProcessModule (mscorlib.ni.dll), System.Diagnostics.ProcessModule
(ole32.dll), System.Diagnostics.ProcessModule (uxtheme.dll),
System.Diagnostics.ProcessModule (tiptsf.dll), System.Diagnostics.ProcessModule
(OLEAUT32.dll), System.Diagnostics.ProcessModule (CRYPTSP.dll),
System.Diagnostics.ProcessModule (rsaenh.dll), System.Diagnostics.ProcessModule
(bcrypt.dll), System.Diagnostics.ProcessModule (CRYPTBASE.dll),
(...)

PowerShell 技能连载 - 立刻关闭显示器

如果您想运行一个长时间执行的脚本,何不先关闭显示器,而是傻傻地等着超时以后显示屏保呢?

以下是一个简单的函数,能够立即关闭显示器。只需要移动鼠标或按任意键就可以唤醒:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Set-DisplayOff
{
$code = @"
using System;
using System.Runtime.InteropServices;
public class API
{
[DllImport("user32.dll")]
public static extern
int SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
}
"@
$t = Add-Type -TypeDefinition $code -PassThru
$t::SendMessage(0xffff, 0x0112, 0xf170, 2)
}

PowerShell 技能连载 - 配置网络适配器

以下是一个简单的例子,演示如何向网络适配器分配 IP 地址、网关,和 DNS 服务器。这段脚本列出所有活动的网络适配器。当您选择一项并点击确认,脚本将把硬编码的地址赋给网络适配器。

请注意一下脚本只是模拟改变网络配置。如果您希望真的改变网络设置,请移除 -WhatIf 参数:

1
2
3
4
5
6
7
8
9
10
11
12
$NewIP = '192.168.2.12'
$NewGateway = '192.168.2.2'
$NewDNS = '8.8.8.8'
$Prefix = 24

$adapter = Get-NetAdapter |
Where-Object Status -eq 'Up' |
Out-GridView -Title 'Select Adapter to Configure' -OutputMode Single
$index = $adapter.InterfaceIndex

New-NetIPAddress -InterfaceIndex $index -IPAddress $NewIP -DefaultGateway $NewGateway -PrefixLength $Prefix -AddressFamily IPv4 -WhatIf
Set-DNSClientServerAddress –InterfaceIndex $index –ServerAddresses $NewDNS -whatIf

PowerShell 技能连载 - 免费的 PowerShell 帮助手册

即便是有经验的 PowerShell 用户也会经常忽略 PowerShell 强大的帮助系统,它类似 Linux 中的 man page。您所需要做的只是一次性下载帮助文件。要下载帮助文件,您需要在提升权限的 PowerShell 中运行以下代码:

1
PS> Update-Help -UICulture en-us -Force

当帮助文件下载完以后,以下是一个很棒的基础列表,展示 PowerShell 语言的几乎所有细节:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
PS> Get-Help about* | Select Name, Synopsis

Name Synopsis
---- --------
about_ActivityCommonParameters Describes the parameters that Windows...
about_Aliases Describes how to use alternate names ...
about_Arithmetic_Operators Describes the operators that perform ...
about_Arrays Describes arrays, which are data stru...
about_Assignment_Operators Describes how to use operators to ass...
about_Automatic_Variables Describes variables that store state ...
about_Break Describes a statement you can use to ...
about_Checkpoint-Workflow Describes the Checkpoint-Workflow act...
about_CimSession Describes a CimSession object and the...
about_Classes Describes how you can use classes to ...
about_Command_Precedence Describes how Windows PowerShell dete...
about_Command_Syntax Describes the syntax diagrams that ar...
about_Comment_Based_Help Describes how to write comment-based ...
about_CommonParameters Describes the parameters
...

您甚至可疑用这行代码创建您自己的 PowerShell 帮助查看器:

1
2
3
4
5
6
Get-Help about* |
Select-Object -Property Name, Synopsis |
Out-GridView -Title 'Select Topic' -OutputMode Multiple |
ForEach-Object {
Get-Help -Name $_.Name -ShowWindow
}

当运行这行代码时,PowerShell 将搜索帮助主题并打开一个网格视图。只需要按住 CTRL 并选择所有您想阅读的主题,然后单击确认。选中的主题将会在独立的帮助查看窗口中打开。

PowerShell 技能连载 - 用 PowerShell 管理 Windows 10 的缺省 APP

Windows 10 缺省安装了一系列 APP,而且即使您手动卸载了它们,它们有可能在下一个重大 Windows 10 更新的时候又回来。

PowerShell 也可以移除这些 Windows 10 APP。您可以在任意时候重新运行脚本,来确保要清除的 APP 确实被删除了。例如这行代码将移除 3D Builder APP:

1
PS> Get-AppxPackage *3dbuilder* | Remove-AppxPackage

有许多类似 http://www.thewindowsclub.com/remove-built-windows-10-apps-users-using-powershell-script 的网站,提供许多更多如何移除特定的 Windows 10 APP 的例子。

PowerShell 技能连载 - 使用神奇的脚本块参数

在前一个例子中我们演示了 Rename-ItemNewName 的神奇功能。它可以接受一个新的文件名,也可以接受一个脚本块。脚本块可以用来批量命名大量文件。

我们现在来看看 PowerShell 函数如何实现这种神奇的参数!以下是一个定义了两个参数的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function Test-MagicFunc
{
[CmdletBinding()]
param
(

[Parameter(Mandatory=$true, Position=0, ValueFromPipeline)]
[string]
$Par1,



[Parameter(ValueFromPipelineByPropertyName)]
[string]
$Par2
)


begin
{
Write-Warning "Starting..."
}

process
{
Write-Warning "Processing Par1=$Par1 and Par2=$Par2"
}

end
{
Write-Warning "Ending..."
}
}

您可以把它当作一个传统的独立函数运行:

1
2
3
4
PS>  Test-MagicFunc -Par1 100 -Par2 50
WARNING: Starting...
WARNING: Processing Par1=100 and Par2=50
WARNING: Ending...

您也可以通过管道输出来运行它,并且传入一个固定的第二参数:

1
2
3
4
5
6
7
PS> 1..4 |  Test-MagicFunc -Par2 99
WARNING: Starting...
WARNING: Processing Par1=1 and Par2=99
WARNING: Processing Par1=2 and Par2=99
WARNING: Processing Par1=3 and Par2=99
WARNING: Processing Par1=4 and Par2=99
WARNING: Ending...

但是您也可以对的第二个参数传入一个脚本块,该脚本块引用了从管道收到的对象:

1
2
3
4
5
6
7
PS> 1..4 |  Test-MagicFunc -Par2 { $_ * $_ }
WARNING: Starting...
WARNING: Processing Par1=1 and Par2=1
WARNING: Processing Par1=2 and Par2=4
WARNING: Processing Par1=3 and Par2=9
WARNING: Processing Par1=4 and Par2=16
WARNING: Ending...

事实证明,这个魔法十分简单:Par2 的参数定义显示它可以接受管道输入。它不关心是由属性名输入 (ValueFromPipelineByPropertyName) 还是通过值输入 (ValueFromPipeline)。在这些例子中,当您将一个脚本块传给参数时,PowerShell 将该脚本块当作管道输入值的接口:$_ 引用输入的对象,脚本块可以使用任何需要的代码来计算需要绑定到参数的值。

PowerShell 技能连载 - 批量重命名图片

重命名单个文件可以很容易地用 Rename-Item 实现,但是有些时候 cmdlet 参数可以更聪明地使用,帮您实现批量自动化。

例如,假设您的照片文件夹中有大量的照片:

1
2
$path = [Environment]::GetFolderPath('MyPictures')
Get-ChildItem -Path $path -Filter *.png

如果您想对它们命名,例如在前面加一个序号,您需要设计一个循环,例如这样:

1
2
3
4
5
6
7
8
9
$path = [Environment]::GetFolderPath('MyPictures')
$counter = 0
$files = Get-ChildItem -Path $path -Filter *.png
$files |
ForEach-Object {
$counter++
$newname = '{0} - {1}' -f $counter, $_.Name
Rename-Item -Path $_.FullName -NewName $newname -WhatIf
}

还有一个简单得多的解决方案:-NewName 参数也可以接受一个脚本块。每当一个元素通过管道传给 Rename-Item,脚本块就会执行一次。代码可以简化为:

1
2
3
4
5
6
7
$path = [Environment]::GetFolderPath('MyPictures')
$counter = 0
$files = Get-ChildItem -Path $path -Filter *.png
$files | Rename-Item -NewName {
$script:counter++
'{0} - {1}' -f $counter, $_.Name
} -WhatIf

还有一处重要的区别:Rename-Item 执行的脚本块是在它自己的作用域中运行的,所以如果您希望使用一个递增的计数器,那么需要在变量名之前增加一个 script:,这样变量就作用在脚本作用域上。

警告:在某些 PowerShell 版本中有一个不友好的 bug,重命名文件将改变 Get-ChildItem 的输出,所以如果您直接将 Get-ChildItem 的结果通过管道传给 Rename-Item,您可能会遇到无限死循环,文件会一直被重命名直到文件路径长度超过限制。要安全地使用它,请确保在将变量传给 Rename-Item 之前将 Get-ChildItem 的结果保存到变量中!