PowerShell 技能连载 - 查找不合规的命令动词

Cmdlet 和函数只能用认可的动词以便于用户查找命令,并且保持一致性。

以下是一个快速的审计代码,能够显示不符合这个规定的所有命令:

1
2
3
4
$approved = Get-Verb | Select-Object -ExpandProperty Verb

Get-Command -CommandType Cmdlet, Function |
Where-Object { $approved -notcontains $_.Verb }

这里返回的是所有不符合规定或根本没有命令动词的 cmdlet 和函数。

PowerShell 技能连载 - 最常用的动词

让我们来看看您的 PowerShell 中哪个命令动词是最常用的:

1
2
3
Get-Command -CommandType cmdlet, function |
Group-Object -Property Verb |
Sort-Object -Property Count -Descending

这是我们系统的输出结果:

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
Count Name                      Group
----- ---- -----
456 Get {Get-AppBackgroundTask, Get-AppvVirtualProcess...
210 Set {Set-AssignedAccess, Set-AutologgerConfig, Set...
120 Remove {Remove-AutologgerConfig, Remove-BCDataCacheEx...
102 New {New-AutologgerConfig, New-DAEntryPointTableIt...
72 Enable {Enable-BCDistributed, Enable-BCDowngrading, E...
70 {A:, ABC, AfterAll, AfterAll...}
67 Add {Add-BCDataCacheExtension, Add-BitLockerKeyPro...
65 Disable {Disable-BC, Disable-BCDowngrading, Disable-BC...
28 Start {Start-Animation, Start-AppBackgroundTask, Sta...
26 Update {Update-Disk, Update-DscConfiguration, Update-...
24 Clear {Clear-AssignedAccess, Clear-BCCache, Clear-Bi...
22 Export {Export-Application, Export-BCCachePackage, Ex...
22 Invoke {Invoke-AsWorkflow, Invoke-Background, Invoke-...
20 Import {Import-BCCachePackage, Import-BCSecretKey, Im...
19 Stop {Stop-DscConfiguration, Stop-Dtc, Stop-DtcTran...
19 Rename {Rename-DAEntryPointTableItem, Rename-MaskingS...
19 Test {Test-Ancestor, Test-Descendent, Test-Dtc, Tes...
16 Register {Register-ClusteredScheduledTask, Register-Dns...
15 ConvertTo {ConvertTo-DataTemplate, ConvertTo-GridLength,...
14 Write {Write-DtcTransactionsTraceSession, Write-Prin...
14 Show {Show-Clock, Show-Details, Show-NetFirewallRul...
14 Reset {Reset-BC, Reset-DAClientExperienceConfigurati...
13 Unregister {Unregister-AppBackgroundTask, Unregister-Clus...
11 Out {Out-GridViewVertical, Out-Notepad, Out-Voice,...
11 Copy {Copy-DependencyProperty, Copy-NetFirewallRule...
9 Send {Send-EtwTraceSession, Send-PSCONFConfirmation...
9 Find {Find-Command, Find-DscResource, Find-Module, ...
9 ConvertFrom {ConvertFrom-SddlString, ConvertFrom-TypeToScr...
7 Save {Save-Module, Save-NetGPO, Save-NetworkSwitchC...
7 Repair {Repair-FileIntegrity, Repair-VirtualDisk, Rep...
7 Debug {Debug-FileShare, Debug-MMAppPrelaunch, Debug-...
7 Format {Format-Hex, Format-Volume, Format-Custom, For...
6 Suspend {Suspend-BitLocker, Suspend-PrintJob, Suspend-...
6 Install {Install-Dtc, Install-ISEPreviewShortcut, Inst...
6 Publish {Publish-BCFileContent, Publish-BCWebContent, ...
6 Resume {Resume-BitLocker, Resume-PrintJob, Resume-Bit...
6 Move {Move-Control, Move-SmbWitnessClient, Move-App...
5 Restore {Restore-DscConfiguration, Restore-NetworkSwit...
5 Restart {Restart-NetAdapter, Restart-PcsvDevice, Resta...
5 Select {Select-Date, Select-UIType, Select-Object, Se...
5 Mount {Mount-DiskImage, Mount-AppvClientConnectionGr...
4 Uninstall {Uninstall-Dtc, Uninstall-Module, Uninstall-Sc...
4 Disconnect {Disconnect-IscsiTarget, Disconnect-VirtualDis...
4 Connect {Connect-IscsiTarget, Connect-VirtualDisk, Con...
4 Receive {Receive-DtcDiagnosticTransaction, Receive-Job...
4 Unblock {Unblock-FileShareAccess, Unblock-SmbShareAcce...
4 Assert {Assert-MockCalled, Assert-MockCalled, Assert-...
4 Wait {Wait-Debugger, Wait-Event, Wait-Job, Wait-Pro...
3 Complete {Complete-BitsTransfer, Complete-DtcDiagnostic...
3 Resize {Resize-Partition, Resize-StorageTier, Resize-...
3 Optimize {Optimize-StoragePool, Optimize-Volume, Optimi...
3 Initialize {Initialize-Disk, Initialize-EventHandler, Ini...
3 Expand {Expand-Archive, Expand-WindowsCustomDataImage...
3 Dismount {Dismount-DiskImage, Dismount-AppxVolume, Dism...
3 Close {Close-Control, Close-SmbOpenFile, Close-SmbSe...
3 Convert {Convert-Alias, Convert-Path, Convert-String}
2 Use {Use-Transaction, Use-WindowsUnattend}
2 Undo {Undo-DtcDiagnosticTransaction, Undo-Transaction}
2 Trace {Trace-Command, Trace-SteroidsOutput}
2 Split {Split-Path, Split-WindowsImage}
2 Resolve {Resolve-DnsName, Resolve-Path}
2 Measure {Measure-Command, Measure-Object}
2 Join {Join-DtcDiagnosticResourceManager, Join-Path}
2 Exit {Exit-PSHostProcess, Exit-PSSession}
2 Read {Read-PrinterNfcTag, Read-Host}
2 Block {Block-FileShareAccess, Block-SmbShareAccess}
2 Enter {Enter-PSHostProcess, Enter-PSSession}
2 Revoke {Revoke-FileShareAccess, Revoke-SmbShareAccess}
2 Do {Do-Something, Do-SomethingWeird}
2 Sync {Sync-NetIPsecRule, Sync-AppvPublishingServer}
2 Edit {Edit-StringList, Edit-CIPolicyRule}
2 Hide {Hide-UIElement, Hide-VirtualDisk}
2 Grant {Grant-FileShareAccess, Grant-SmbShareAccess}
1 Unlock {Unlock-BitLocker}
1 Unpublish {Unpublish-AppvClientPackage}
1 Unprotect {Unprotect-CmsMessage}
1 Open {Open-NetGPO}
1 Lock {Lock-BitLocker}
1 Tee {Tee-Object}
1 Switch {Switch-Certificate}
1 Sort {Sort-Object}
1 Backup {Backup-BitLockerKeyProtector}
1 Protect {Protect-CmsMessage}
1 Compress {Compress-Archive}
1 Pop {Pop-Location}
1 Merge {Merge-CIPolicy}
1 Encrypt {Encrypt-Text}
1 Limit {Limit-EventLog}
1 Group {Group-Object}
1 ForEach {ForEach-Object}
1 Confirm {Confirm-SecureBootUEFI}
1 Decrypt {Decrypt-Text}
1 Compare {Compare-Object}
1 Checkpoint {Checkpoint-Computer}
1 Push {Push-Location}
1 Where {Where-Object}

更有趣的是,以下是 PowerShell cmdlet 前六个最常用的动词:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PS C:\> Get-Command -CommandType cmdlet, function  |
Group-Object -Property Verb |
Sort-Object -Property Count -Descending |
Where-Object { $_.Name } |
Select-Object -First 6 -Property Count, Name

Count Name
----- ----
456 Get
210 Set
120 Remove
102 New
72 Enable
67 Add

所以说这头六个动词只占动词总数的 6%,但是占了所有命令的 60% 以上。

PowerShell 技能连载 - 安全地对文本加解密

当您加密保密信息时,主要的问题是要寻找一个合适的密钥。一个特别安全的密钥是您的 Windows 身份,它和您的计算机身份绑定。这可以用来在特定的机器上加密敏感的个人信息。

以两个函数演示了如何实现:

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
function Decrypt-Text
{

param
(
[String]
[Parameter(Mandatory,ValueFromPipeline)]
$EncryptedText
)
process
{
$secureString = $EncryptedText | ConvertTo-SecureString
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString)
[System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
}
}

function Encrypt-Text
{

param
(
[String]
[Parameter(Mandatory,ValueFromPipeline)]
$Text
)
process
{
$Text |
ConvertTo-SecureString -AsPlainText -Force |
ConvertFrom-SecureString
}
}

'PowerShell Rocks' | Encrypt-Text
'Hello, World!' | Encrypt-Text | Decrypt-Text

您可以将密文安全地保存到文件里。只有您可以读取并解密该文件,而且只能在加密用的电脑上完成。

PowerShell 技能连载 - 同时使用 -Force 和 -WhatIf 时请注意

-WhatIf 通用参数可以打开模拟模式,这样一个 cmdlet 执行的时候并不会改变任何东西,而是汇报它“将会”改变什么。它能工作得很好, 除非开发者没有正确地实现 -WhatIf

有一种比较少见的情况:当您同时指定了 -Force-WhatIf 参数,正确的结果是 -WhatIf 具有更高的优先级。有一些开发者过于关注 -Force 的功能,而让 -Force 优先级更高。例如请试试 Remove-SmbShare

PowerShell 技能连载 - 创建文件共享

在 Server 2012 R2 和 Windows .1 中,有许多有用的新模块,包含了许多新的 cmdlet,例如 New-SmbShare 可以快速地创建新的文件共享。

如果您没有这些 cmdlet,您通常可以使用 WMI。那需要更多的研究和搜索,但是一旦您有了一个代码模板,它就能很好地工作了。

例如要以管理员身份创建一个新的文件共享,试试以下代码:

1
2
3
4
5
6
7
$share = [wmiclass]"Win32_Share"
$path = 'c:\logs'
$name = 'LogShare'
$maxallowed = 10
$description = 'Place log files here'

$share.Create( $path, $name, 0, $maxallowed,$description,$null,$null)

PowerShell 技能连载 - 使用自定义作用域来屏蔽任何输出

昨天我们看了自定义作用域能够自动还原变量并在您的代码之后清除现场。

自定义作用域也可以用来忽略域里任何一段代码输出的任何结果。要实现它,请使用这样的解构:$null = .{[code]}。无论您在方括号里执行什么代码,您创建的所有的变量和函数在域外都能使用,但是不会产生任何输出。

让我们看看这个函数:

1
2
3
4
5
6
7
function Out-Voice ($Text)
{
$sapi = New-Object -ComObject Sapi.SpVoice
$sapi.Speak($Text)
}

Out-Voice -Text 'Hello, Dude.'

当你运行它时,它将能播放语音,但也输出了数字“1”。所以 Speak() 方法会造成这样的现象——当您的代码变得庞大而复杂时,有许多地方在输出不必要的数字。

以下是一个极简单的“补丁”函数能产生相同的小郭,但是保证不会返回任何值:

1
2
3
4
5
6
7
8
9
function Out-Voice ($Text)
{
$null = . {
$sapi = New-Object -ComObject Sapi.SpVoice
$sapi.Speak($Text)
}
}

Out-Voice -Text 'Hello, Dude.'

PowerShell 技能连载 - 使用自定义域

当您改变变量时,您可能需要在稍后清除它们并且确保它们回退到缺省值——用自定义作用域就可以做到。昨天,我们学习了如何处理控制台程序的错误。并且回顾那段代码,您会发现重置 $ErrorActionPreference 系统变量要费很多事:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
try
{
# set the preference to STOP
$old = $ErrorActionPreference
$ErrorActionPreference = 'Stop'
# RUN THE CONSOLE EXE THAT MIGHT EMIT AN ERROR,
# and redirect the error channel #2 to the
# output channel #1
net user doesnotexist 2>&1
}

catch [System.Management.Automation.RemoteException]
{
# catch the error emitted by the EXE,
# and do what you want
$errmsg = $_.Exception.Message
Write-Warning $errmsg
}

finally
{
# reset the erroractionpreference to what it was before
$ErrorActionPreference = $old
}

一个简单得多的办法是使用自定义作用域:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
& {
try
{
# set the preference to STOP
$ErrorActionPreference = 'Stop'
# RUN THE CONSOLE EXE THAT MIGHT EMIT AN ERROR,
# and redirect the error channel #2 to the
# output channel #1
net user doesnotexist 2>&1
}

catch [System.Management.Automation.RemoteException]
{
# catch the error emitted by the EXE,
# and do what you want:
$errmsg = $_.Exception.Message
Write-Warning $errmsg
}
}

${[code]} 这段代码创建了一个新的作用域,并且任何在其中定义的变量都会在退出该作用域时删除。这是为何在上述例子中,$ErrorActionPreference 能够自动还原为它之前的值。

PowerShell 技能连载 - 捕获 Native EXE 的错误

是否想知道如何捕获 native 控制台 EXE 程序的错误?PowerShell 的错误处理器只能处理 .NET 代码的错误。

这段代码是捕获控制台应用程序错误的框架:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
try
{
# set the preference to STOP
$old = $ErrorActionPreference
$ErrorActionPreference = 'Stop'
# RUN THE CONSOLE EXE THAT MIGHT EMIT AN ERROR,
# and redirect the error channel #2 to the
# output channel #1
net user doesnotexist 2>&1
}

catch [System.Management.Automation.RemoteException]
{
# catch the error emitted by the EXE,
# and do what you want
$errmsg = $_.Exception.Message
Write-Warning $errmsg
}

finally
{
# reset the erroractionpreference to what it was before
$ErrorActionPreference = $old
}

一旦控制台程序发出一个错误,它就会输出到控制台的 #2 通道。由于示例代码中该通道直接重定向到普通的 output,所以 PowerShell 能接收到它。当 ErrorActionPreference 设成 “Stop“ 时,PowerShell 会将任何该通道的输入数据转发到一个 .NET RemoteException,这样您就可以捕获它。

1
WARNING: The user name  could not be found.

PowerShell 技能连载 - 高级错误处理:重新抛出异常

在处理错误时,您有时会希望将原始的异常替换成您自己的。以下是一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function Do-Something
{
# function uses internal error handling
try
{
Get-Process -Name NotThereOhWell -ErrorAction Stop
}
# catch this error type
catch [Microsoft.PowerShell.Commands.ProcessCommandException]
{
$oldE = $_.Exception

# handle the error, OR SHOWN HERE: issue a new exception to the caller
$newE = New-Object -TypeName System.InvalidOperationException('Do-Something: A fatal error occured', $oldE)
Throw $newE
}
}

# function will encounter an internal error
# error message shows error message generated by function instead
Do-Something

调用者看到的内容如下:

1
2
3
4
5
6
7
PS C:\>  Do-Something
Do-Something: A fatal error occured
At line:18 char:5
+ Throw $newException
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], InvalidOperationException
+ FullyQualifiedErrorId : Do-Something: A fatal error occured

如果调用者也用一个错误处理函数来接收它,则会出现这种情况:

1
2
3
4
5
6
7
8
9
10
11
try
{
Do-Something
}
catch [System.InvalidOperationException]
{
[PSCustomObject]@{
Message = $_.Exception.Message
Originalmessage = $_.Exception.InnerException.Message
}
}

结果看起来如下:

1
2
3
4
5
Message                             Originalmessage
------- ---------------
Do-Something: A fatal error occured Cannot find a process with the name
"NotThereOhWell". Verify the process name
and call the cmdlet again.

这样调用者可以看到返回的错误信息,并且经过内部处理之后,还可以传递原始的错误信息。

PowerShell 技能连载 - 用其他身份启动程序

假设您想以不同的身份打开多个 PowerShell 控制台,或以其他人的身份打开任何程序。

要实现这个目标,您需要以其他人的身份登录,这很明显是个负担。以下是将凭据以安全的方式保存到文件的方法:密码采用您的身份和您的机器加密成密文。只有保存它们的那个人可以取回它,而且只能在保存该文件的机器上操作:

1
2
# saving credential securely to file
Get-Credential | Export-Clixml -Path "$home\login.xml"

这个凭据将保存到用户配置中。如果您希望保存到其它地方,请改变路径。喜欢保存多少份,就调用多少次该方法。

下一步,假设您加载了一个保存的凭据,并且使用该身份启动了一个程序:

1
2
3
4
# getting back saved credential
$cred = Import-Clixml -Path "$home\login.xml"
# launch application
Start-Process -FilePath powershell -Credential $cred -Work c:\ -LoadUserProfile

这将以您之前指定的用户身份创建一个新的 PowerShell 实例——无需手动登录。