PowerShell 技能连载 - 添加前导零

如果您需要数字前面添加前导零,例如对于服务器名,以下是两种实现方式。第一,您可以将数字转换为字符串,然后用 PadLeft() 函数将字符串填充到指定的长度:

1
2
3
4
5
6
7
8
9
10
11
12
$number = 76
$leadingZeroes = 8

$number.Tostring().PadLeft($leadingZeroes, '0')

Or, you can use the -f operator:


$number = 76
$leadingZeroes = 8

"{0:d$leadingZeroes}" -f $number

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
function Show-MessageBox
{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$true,ValueFromPipeline=$false)]
[String]
$Text,

[Parameter(Mandatory=$true,ValueFromPipeline=$false)]
[String]
$Caption,

[Parameter(Mandatory=$true,ValueFromPipeline=$false)]
[Windows.MessageBoxButton]
$Button,

[Parameter(Mandatory=$true,ValueFromPipeline=$false)]
[Windows.MessageBoxImage]
$Icon

)

process
{
try
{
[System.Windows.MessageBox]::Show($Text, $Caption, $Button, $Icon)
}
catch
{
Write-Warning "Error occured: $_"
}
}
}

以下是它的使用方法:

1
PS> Show-MessageBox -Text 'Do you want to reboot now?' -Caption Reboot -Button YesNoCancel -Icon Exclamatio

PowerShell 技能连载 - 显示输入框

如果您想弹出一个快速而粗糙的输入框,提示用户输入数据,您可以通过 Microsoft Visual Basic 并且“借用”它的 InputBox 控件:

1
2
3
Add-Type -AssemblyName Microsoft.VisualBasic
$result = [Microsoft.VisualBasic.Interaction]::InputBox("Enter your Name", "Name", $env:username)
$result

但是请注意,这种方法有一些局限性:该输入框可能在您的 PowerShell 窗口之下打开,而且在高分辨率屏中,它的缩放可能不正确。

PowerShell 技能连载 - 快速读取文本文件

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
# make sure this file exists, or else
# pick a different text file that is
# very large
$path = 'C:\Windows\Logs\DISM\dism.log'

# slow reading line-by-line
Measure-Command {
$text = Get-Content -Path $Path
}

# fast reading entire text as one large string
Measure-Command {
$text = Get-Content -Path $Path -Raw
}

# fast reading text as string array with one
# array element per line
Measure-Command {
$text = Get-Content -Path $Path -ReadCount 0
}

# reading entire text with .NET
# no advantage over -Raw
Measure-Command {
$text = [System.IO.File]::ReadAllText($path)
}

PowerShell 技能连载 - 从本地时间以 ISO 格式创建 UTC 时间

如果您的工作是跨国跨时区的,您可能会需要使用 UTC 时间来代替本地时间。要确保时间格式是语言中性的,我们推荐使用 ISO 格式。以下是使用方法:

1
2
$date = Get-Date
$date.ToUniversalTime().ToString('yyyy-MM-dd HH:mm:ss')

PowerShell 技能连载 - 巧妙地读取事件日志(第 2 部分)

在前一个技能中我们演示了如何使用 ReplacementStrings 读取从 Get-EventLog 中收到的详细的事件日志信息。它工作得很完美,但是 Get-EventLog 职能读取“传统的”Windows 日志。在现代的 Windows 版本中还有许多额外的日志。

这些日志可以通过 Get-WinEvent 读取,而且有许多信息可以发掘。例如,要获取已安装的更新列表,请试试这段代码:

1
2
3
$filter = @{ ProviderName="Microsoft-Windows-WindowsUpdateClient"; Id=19 }

Get-WinEvent -FilterHashtable $filter | Select-Object -ExpandProperty Message -First 4

请注意这只是一个例子。通过以上代码,您可以查询您关心的任意事件 ID 的日志。例如以上代码,可以获取最新安装的 4 条更新:

1
2
3
4
5
6
7
8
9
PS> . 'C:\Users\tobwe\Documents\PowerShell\Untitled5.ps1' <# script is not saved yet #>
Installation Successful: Windows successfully installed the following update: Definitionsupdate für
Windows Defender Antivirus – KB2267602 (Definition 1.269.69.0)
Installation Successful: Windows successfully installed the following update: 9WZDNCRFJ1XX-FITBIT.F
ITBIT
Installation Successful: Windows successfully installed the following update: Definitionsupdate für
Windows Defender Antivirus – KB2267602 (Definition 1.269.28.0)
Installation Successful: Windows successfully installed the following update: 9WZDNCRFHVQM-MICROSOF
T.WINDOWSCOMMUNICATIONSAPPS

然而,这只是文本,而且要将它转换为一份已安装的更新的漂亮的报告并不容易。通过 Get-EventLog,类似我们之前的技能介绍的,您可以使用 ReplacementStrings 来方便地存取纯净的信息。但是 Get-WinEvent 没有 ReplacementStrings

然而,有一个名为 Properties 的属性。以下是如何将属性转换为类似 ReplacementStrings 的数组的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$filter = @{ ProviderName="Microsoft-Windows-WindowsUpdateClient"; Id=19 }

Get-WinEvent -FilterHashtable $filter |
ForEach-Object {
# create a ReplacementStrings array
# this array holds the information that is inserted
# into the event message template text
$ReplacementStrings = $_.Properties | ForEach-Object { $_.Value }

# return a new object with the required information
[PSCustomObject]@{
Time = $_.TimeCreated
# index 0 contains the name of the update
Name = $ReplacementStrings[0]
User = $_.UserId.Value
}
}

这段代码返回以安装更新的美观的列表:

Time                Name
----                ----
25.05.2018 09:00:20 Definitionsupdate für Windows Defender Antivirus – KB2267602 (Definition 1....
25.05.2018 07:59:44 9WZDNCRFJ1XX-FITBIT.FITBIT
24.05.2018 11:04:15 Definitionsupdate für Windows Defender Antivirus – KB2267602 (Definition 1....
24.05.2018 08:36:26 9WZDNCRFHVQM-MICROSOFT.WINDOWSCOMMUNICATIONSAPPS
24.05.2018 08:34:30 9N4WGH0Z6VHQ-Microsoft.HEVCVideoExtension
24.05.2018 08:34:24 9WZDNCRFJ2QK-ZDFGemeinntzigeAnstaltdes.ZDFmediathek
23.05.2018 11:57:42 Definitionsupdate für Windows Defender Antivirus – KB2267602 (Definition 1....
23.05.2018 07:37:11 9WZDNCRFHVQM-MICROSOFT.WINDOWSCOMMUNICATIONSAPPS
23.05.2018 07:36:57 9WZDNCRFJ3PT-MICROSOFT.ZUNEMUSIC
23.05.2018 04:01:11 Definitionsupdate für Windows Defender Antivirus – KB2267602 (Definition 1....
22.05.2018 12:26:55 Definitionsupdate für Windows Defender Antivirus – KB2267602 (Definition 1....
22.05.2018 08:34:28 9NBLGGH5FV99-Microsoft.MSPaint
22.05.2018 08:33:25 9WZDNCRFJ364-MICROSOFT.SKYPEAPP

PowerShell 技能连载 - 巧妙地读取事件日志(第 1 部分)

当您使用 PowerShell 来查询事件,缺省情况下获取到的是日志信息的文本消息。例如,如果您想知道谁登录了您的计算机,您可以使用类似这样的代码(需要管理员权限):

1
2
Get-EventLog -LogName Security -InstanceId 4624 |
Select-Object -Property TimeGenerated, Message

结果大概类似这样:

25.04.2018 07:48:41 An account was successfully logged on....
25.04.2018 07:48:40 An account was successfully logged on....
24.04.2018 18:18:17 An account was successfully logged on....
...

这并不是很直观,因为 PowerShell 缩短了输出内容。在类似这样的情况下,您可能需要将结果用管道传给 Format-List

1
2
3
Get-EventLog -LogName Security -InstanceId 4624 |
Select-Object -Property TimeGenerated, Message |
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
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
PS> Get-EventLog -LogName Security -InstanceId 4624 |
Select-Object -Property TimeGenerated, Message -first 1 |
Format-List




TimeGenerated : 25.05.2018 11:39:29
Message : An account was successfully logged on.

Subject:
Security ID: S-1-5-18
Account Name: DESKTOP-7AAMJLF$
Account Domain: WORKGROUP
Logon ID: 0x3e7

Logon Information:
Logon Type: 5
Restricted Admin Mode: -
Virtual Account: %%1843
Elevated Token: %%1842

Impersonation Level: %%1833

New Logon:
Security ID: S-1-5-18
Account Name: SYSTEM
Account Domain: NT-AUTORITÄT
Logon ID: 0x3e7
Linked Logon ID: 0x0
Network Account Name: -
Network Account Domain: -
Logon GUID: {00000000-0000-0000-0000-000000000000}

Process Information:
Process ID: 0x328
Process Name: C:\Windows\System32\services.exe

Network Information:
Workstation Name: -
Source Network Address: -
Source Port: -

Detailed Authentication Information:
Logon Process: Advapi
Authentication Package: Negotiate
Transited Services: -
Package Name (NTLM only): -
Key Length: 0

This event is generated when a logon session is created. It is
generated on the computer that was accessed.

The subject fields indicate the account on the local system
which requested the logon. This is most commonly a service
such as the Server service, or a local process such as
Winlogon.exe or Services.exe.

The logon type field indicates the kind of logon that
occurred. The most common types are 2 (interactive) and 3
(network).

The New Logon fields indicate the account for whom the new
logon was created, i.e. the account that was logged on.

The network fields indicate where a remote logon request
originated. Workstation name is not always available and may
be left blank in some cases.

The impersonation level field indicates the extent to which a
process in the logon session can impersonate.

The authentication information fields provide detailed
information about this specific logon request.
- Logon GUID is a unique identifier that can be used to
correlate this event with a KDC event.
- Transited services indicate which intermediate services
have participated in this logon request.
- Package name indicates which sub-protocol was used among
the NTLM protocols.
- Key length indicates the length of the generated session
key. This will be 0 if no session key was requested.

这个结果很难处理。如果您希望基于这段文本做一些自动化处理,您需要解析这段文本。

有一个简单得多的方法:您见到的消息只是一个文本模板,Windows 以“替换字符串”的方式插入相关的信息。他们是从 Get-0EventLog 接收到的事件数据的一部分。该数据存在一个数组中,整个数组对应一个事件 ID 的信息。

当您确定了哪个信息存放在哪个数组元素中,要解析出您关心的信息十分容易:

1
2
3
4
5
6
7
8
9
10
Get-EventLog -LogName Security -InstanceId 4624 |
ForEach-Object {
# translate the raw data into a new object
[PSCustomObject]@{
Time = $_.TimeGenerated
User = "{0}\{1}" -f $_.ReplacementStrings[5], $_.ReplacementStrings[6]
Type = $_.ReplacementStrings[10]
Path = $_.ReplacementStrings[17]
}
}

当您运行这一小段代码时,它返回只包含您需要的、美观的验证信息:

12.05.2018 17:38:58 SYSTEM\NT-AUTORITÄT                     Negotiate C:\Windows\System32\services.exe
12.05.2018 17:38:58 tobweltner@zumsel.local\InternalAccount Negotiate C:\Windows\System32\svchost.exe
12.05.2018 17:38:58 SYSTEM\NT-AUTORITÄT                     Negotiate C:\Windows\System32\services.exe
12.05.2018 17:38:58 SYSTEM\NT-AUTORITÄT                     Negotiate C:\Windows\System32\services.exe
12.05.2018 17:38:53 SYSTEM\NT-AUTORITÄT                     Negotiate C:\Windows\System32\services.exe

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