PowerShell 技能连载 - 研究 ConfirmImpact(第 1 部分:用户视角)

在 PowerShell 中,默认情况下,$ConfimPreference 变量设置为 “High“。这个设置控制什么?

1
2
PS> $ConfirmPreference
High

任何 PowerShell 命令(二进制 cmdlet 或函数)都可以设置自己的 “ConfirmImpact“:允许的值为 NoneLowMediumHighConfirmImpact 是一个评估 cmdlet 效果的关键性程度。

默认情况下,当 $ConfirmImpact 设置为 “High“ 时,PowerShell 将在你运行设置了 ConfirmImpactHigh 的cmdlet 或函数时自动要求确认(所以你只会在运行像 AD 账户这样不能轻易恢复的东西的 cmdlets 时看到确认对话框弹出)。

作为用户,您可以调整这个风险缓解系统。如果你在一个十分敏感的生产系统上工作,你可能想把 $ConfirmPreference 降低到 Medium 或甚至 Low,以在运行 PowerShell 命令或脚本时获得更多的确认。当设置为 “Low“时,即使创建一个新文件夹也会触发自动确认:

1
2
3
4
5
6
7
8
9
10
PS> $ConfirmPreference = 'Low'

PS> New-Item -Path c:\testfolder

Confirm
Are you sure you want to perform this action?
Performing the operation "Create File" on target "Destination:
C:\testfolder".
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help
(default is "Y"):

同样地,如果你想运行一个脚本而不被自动确认对话框打断,你可以把 $ConfirmPreference 设置为 “None“,从而关闭这个风险缓解系统。这比为脚本中可能触发确认的每个命令添加 -Confirm:$false 参数来手动覆盖自动确认要高效得多。

$ConfirmPreference 的任何更改都会在关闭当前 PowerShell 会话时恢复为默认值。它们不会自动持久化。如果您想永久更改这些设置,请创建一个配置文件脚本并将所有永久更改添加到这个脚本中。当 PowerShell 启动时,它会自动启动。

这样的配置文件脚本的路径可以在这里找到:

1
2
PS> $profile.CurrentUserAllHosts
C:\Users\USERNAME\OneDrive\Documents\WindowsPowerShell\profile.ps1

PowerShell 技能连载 - 选择最佳方法:单词转大写(第 2 部分)

在 PowerShell 中,当您需要解决一个问题时,有四种不同来源的命令可以选择。在这个迷你系列中,我们依次查看所有方法。要解决的是同一个问题:如何将一个单词的首字母改为大写。请注意,这是我们随意选为例子的一个问题。该解决方案适用于任何想用 PowerShell 解决的问题。

在 PowerShell 中要解决一个问题最简单的方法是使用合适的 PowerShell cmdlet。可以使用 Get-Command 来搜索已有的 cmdlet。在第一部分中我们已经发现没有一个特定的 PowerShell cmdlet 可以做这件事,所以不得不使用低级的方法来解决这个任务。

由于我们现在已经有解决方案,我们只需要将以下代码转为一个全新的 PowerShell cmdlet(这样我们不需要重复发明这个解决方案,以及我们的生产代码变得更精炼切易于理解和回顾):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$text = "thIS is    A  TEST teXT"
# split text in words
$words = $text -split '\s{1,}' |
# use ForEach-Object to break down the problem to solving ONE instance of your problem
# regardless of how many words there are, the following script block deals with
# one word at a time:
ForEach-Object {
$theWord = $_
# use .NET string methods on the object to solve your issue:
$theWord.SubString(0,1).toUpper() + $theWord.SubString(1).ToLower()
}

# the result is a string array. Use the -join operator to turn it into ONE string:
$result = $words -join ' '
$result

将一段代码转为一个可重用的 cmdlet 只需要按照这些固定的步骤:将代码封装在一个函数中,然后定义它的输入(被称为参数)。例如这样:

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
function Convert-CapitalizeWord
{
param
(
[Parameter(Mandatory,ValueFromPipeline)]
$Text
)

process
{
# split text in words
$words = $Text -split '\s{1,}' |
# use ForEach-Object to break down the problem to solving ONE instance of your problem
# regardless of how many words there are, the following script block deals with
# one word at a time:
ForEach-Object {
$theWord = $_
# use .NET string methods on the object to solve your issue:
$theWord.SubString(0,1).toUpper() + $theWord.SubString(1).ToLower()
}

# the result is a string array. Use the -join operator to turn it into ONE string:
$result = $words -join ' '
$result
}
}

请注意头部和尾部的代码。函数中实现逻辑的部分(这个例子中的文本转换部分)仍然保持不变。

当您运行以上代码,PowerShell 设置您的新函数。然后您可以按自己的喜好任意多次调用它,并且由于它是支持管道的,所以您甚至可以通过管道将文本从其它 cmdlet 传给它。如果您想,您可以使用 Get-Content 来读取整个文本并使用 Convert-CapitalizeWord 将每个单词的首字母改为大写——PowerShell 中的函数像一个奇迹,能够使得函数可复用以及可伸缩:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# you get automatic prompts when you forget to submit mandatory arguments:
PS> Convert-CapitalizeWord
cmdlet Convert-CapitalizeWord at command pipeline position 1
Supply values for the following parameters:
Text: heLLO WOrld
Hello World

# you can submit your text to the -Text parameter:
PS> Convert-CapitalizeWord -Text 'this iS a LONG teXT'
This Is A Long Text

# you can pipe as many texts as you like (scalable) via the pipeline:
PS> 'Hello world!', 'someTHING else' | Convert-CapitalizeWord
Hello World!
Something Else

您可以将函数复制/粘贴到需要使用的脚本中。

为了让函数认识并自动加载(类似所有其它“内置”PowerShell cmdlet),并且成为您 shell 的一个永久的命令扩展,您可以将函数存储在模块中。

目前,收获是:通过包装代码在函数,使得代码可重用,自动添加了可扩展性(在上面的例子中,我们现在可以转换在一个调用中转换一个或者上千个字符串),以及使生产脚本代码变得更短,可以专注于它真正想要完成什么。

PowerShell 技能连载 - 选择最佳方法:单词转大写(第 3 部分)

在 PowerShell 中,当您需要解决一个问题时,有四种不同来源的命令可以选择。在这个迷你系列中,我们依次查看所有方法。要解决的是同一个问题:如何将一个单词的首字母改为大写。请注意,这是我们随意选为例子的一个问题。该解决方案适用于任何想用 PowerShell 解决的问题。

在前一部分中,我们已经使用 PowerShell 操作符和通用的字符串方法来解决问题。然而,在代码中通常有一个真理:使用越专用的命令,代码就越简洁。

所以 PowerShell 可以使用另一个来源的命令:从和 PowerShell 一起分发的上千个 .NET 库中选择静态 .NET 方法。有一个比通用操作符和字符串方法简单得多的解决方案:

1
2
3
4
5
6
$text = "thIS is    A  TEST teXT"
[CultureInfo]::InvariantCulture.TextInfo.ToTitleCase($text)
Words that are ALL CAPITALIZED will remain untouched:


This Is A TEST Text

如果您不喜欢有些例外的单词没有被转成首字母大写,那么先将文本转为全小写然后传给该方法:

1
2
3
4
5
PS> [CultureInfo]::InvariantCulture.TextInfo.ToTitleCase('TEST remains aLL uppER Case')
TEST Remains All Upper Case

PS> [CultureInfo]::InvariantCulture.TextInfo.ToTitleCase('TEST remains aLL uppER Case unless you lowerCASE YOUR text beFORE'.ToLower())
Test Remains All Upper Case Unless You Lowercase Your Text Before

如您所见(和这个系列之前的部分相比),空格仍然保持不变,由于我们从没有将文本分割为独立的单词。如果您不喜欢这一点,并且想将多个空格符合并为一个,只需要添加 -replace 运算符。它能将所有字符串整理好:

1
2
3
4
5
6
7
$text = "thIS is    A  TEST teXT"
# title convert and then replace two or more spaces with one space only:
[CultureInfo]::InvariantCulture.TextInfo.ToTitleCase($text.ToLower()) -replace '\s{2,}', ' '
Now this approach returns the exact same result as in our previous parts:


This Is A Test Text

以上代码很短并且很简单,这样您可以直接在代码合适的地方使用它,但是您下星期或者下个月要做相同的转换时还能记得它吗?

所以仍然可以将该代码包装成为一个函数。您可以将我们在第 2 部分中创建的函数升级为更新更有效的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
function Convert-CapitalizeWord
{
param
(
[Parameter(Mandatory,ValueFromPipeline)]
$Text
)

process
{
[CultureInfo]::InvariantCulture.TextInfo.ToTitleCase($text.ToLower()) -replace '\s{2,}', ' '
}
}

当您运行该函数,它将和之前的版本一样灵活和可扩展:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# you get automatic prompts when you forget to submit mandatory arguments:
PS> Convert-CapitalizeWord
cmdlet Convert-CapitalizeWord at command pipeline position 1
Supply values for the following parameters:
Text: heLLO WOrld
Hello World

# you can submit your text to the -Text parameter:
PS> Convert-CapitalizeWord -Text 'this iS a LONG teXT'
This Is A Long Text

# you can pipe as many texts as you like (scalable) via the pipeline:
PS> 'Hello world!', 'someTHING else' | Convert-CapitalizeWord
Hello World!
Something Else

PowerShell 技能连载 - 选择最佳方法:单词转大写(第 1 部分)

在 PowerShell 中,当您需要解决一个问题时,有四种不同来源的命令可以选择。在这个迷你系列中,我们依次查看所有方法。要解决的是同一个问题:如何将一个单词的首字母改为大写。请注意,这是我们随意选为例子的一个问题。该解决方案适用于任何想用 PowerShell 解决的问题。

在 PowerShell 中要解决一个问题最简单的方法是使用合适的 PowerShell cmdlet。可以使用 Get-Command 来搜索已有的 cmdlet。不幸的是,不可能对于任何问题都有完美的 cmdlet。在这种情况中可能找不到合适的 cmdlet。

在这种情况下,PowerShell 任然提供了许多途径来解决问题。在今天的解决方案中,我们使用 PowerShell 运算符和 .NET 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$text = "thIS is    A  TEST teXT"
# split text in words
$words = $text -split '\s{1,}' |
# use ForEach-Object to break down the problem to solving ONE instance of your problem
# regardless of how many words there are, the following script block deals with
# one word at a time:
ForEach-Object {
$theWord = $_
# use .NET string methods on the object to solve your issue:
$theWord.SubString(0,1).toUpper() + $theWord.SubString(1).ToLower()
}

# the result is a string array. Use the -join operator to turn it into ONE string:
$result = $words -join ' '
$result

结果看起来不错:

This Is A Test Text

PowerShell 技能连载 - 选择最佳方法:单词转大写(第 1 部分)

在 PowerShell 中,当您需要解决一个问题时,有四种不同来源的命令可以选择。在这个迷你系列中,我们依次查看所有方法。要解决的是同一个问题:如何将一个单词的首字母改为大写。请注意,这是我们随意选为例子的一个问题。该解决方案适用于任何想用 PowerShell 解决的问题。

在 PowerShell 中要解决一个问题最简单的方法是使用合适的 PowerShell cmdlet。可以使用 Get-Command 来搜索已有的 cmdlet。不幸的是,不可能对于任何问题都有完美的 cmdlet。在这种情况中可能找不到合适的 cmdlet。

在这种情况下,PowerShell 任然提供了许多途径来解决问题。在今天的解决方案中,我们使用 PowerShell 运算符和 .NET 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$text = "thIS is    A  TEST teXT"
# split text in words
$words = $text -split '\s{1,}' |
# use ForEach-Object to break down the problem to solving ONE instance of your problem
# regardless of how many words there are, the following script block deals with
# one word at a time:
ForEach-Object {
$theWord = $_
# use .NET string methods on the object to solve your issue:
$theWord.SubString(0,1).toUpper() + $theWord.SubString(1).ToLower()
}

# the result is a string array. Use the -join operator to turn it into ONE string:
$result = $words -join ' '
$result

结果看起来不错:

This Is A Test Text

PowerShell 技能连载 - 检测电池健康与质量

如果您在使用笔记本电脑,那么可以轻松地询问 WMI 得到电池的状态,例如充电状态。如果多做一点功课,您还可以检查电池的健康并且了解是否该更换电池。

本质上,下面的脚本使用不同的 WMI 类来确定电池的标称容量和实际容量,然后以百分比计算其有效容量。任何低于 80% 的百分比通常表明高度损耗和需要更换电池。

1
2
3
4
5
6
7
8
9
10
$designCap = Get-WmiObject -Class "BatteryStaticData" -Namespace "ROOT\WMI" |
Group-Object -Property InstanceName -AsHashTable -AsString

Get-CimInstance -Class "BatteryFullChargedCapacity" -Namespace "ROOT\WMI" |
Select-Object -Property InstanceName, FullChargedCapacity, DesignedCapacity, Percent |
ForEach-Object {
$_.DesignedCapacity = $designCap[$_.InstanceName].DesignedCapacity
$_.Percent = [Math]::Round( ( $_.FullChargedCapacity*100/$_.DesignedCapacity),2)
$_
}

PowerShell 技能连载 - 使用枚举来解析序号

WMI 是一个简单的获取计算机信息的方法。例如要确定您所使用的计算机类型,是一件很容易的事:

1
2
$info = (Get-CimInstance -ClassName win32_computersystem).PCSystemType
$info

不幸的是,WMI 属性往往返回的是幻数而不是友好的文本,所以当您在笔记本电脑上运行以上代码,将得到结果 “2”。如果返回其它结果,还需要上 google 搜索该数字的含义。

一旦你获得了代码,在 PowerShell 中就有一种简单而有效的方法可以使用枚举将数字转换为友好的文本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum ServerTypes
{
Unspecified
Desktop
Mobile
Workstation
EnterpriseServer
SOHOServer
AppliancePC
PerformanceServer
Maximum
}

[ServerTypes]$info = (Get-CimInstance -ClassName win32_computersystem).PCSystemType
$info

枚举将友好的文本与代码号绑定,默认情况下以 0 开头。上面的枚举将 “Unspecified” 绑定为代码 0,”Mobile” 绑定为代码 2。

By assigning the enum type [ServerTypes] to your result variable, all translating is performed automatically, and cryptic code numbers now show as friendly text.
通过将枚举类型 [ServerTypes] 绑定到您的结果变量,所有转换都会自动进行,而且现在幻数可以转换为友好的文本。

由于 ID 数字在不断变化,因此可能会遇到新的代码,所以必须扩展枚举。

PowerShell 技能连载 - 将 PowerShell 脚本作为命令(第 2 部分)

在上一个技能中,我们讨论了一种扩展 PowerShell 命令集的简易方法。通过将脚本保存到一个文件夹中,并且将文件夹添加到环境变量 $env:path 中,PowerShell 将会识别出该文件夹中的所有脚本并将它们作为新命令。

PowerShell 脚本支持和函数相同的用户参数机制。让我们看看如何将一个使用参数的新的基于脚本的命令加入 PowerShell。

将以下脚本保存到 c:\myPsCommands 目录中的 “New-Password.ps1”。您可能需要先创建该文件夹。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[CmdletBinding()]
param
(
$CapitalLetter = 4,
$Numeric = 1,
$LowerLetter = 3,
$Special = 2
)

$characters = & {
'ABCDEFGHKLMNPRSTUVWXYZ' -as [char[]] |
Get-Random -Count $CapitalLetter

'23456789'.ToCharArray() |
Get-Random -Count $Numeric

'abcdefghkmnprstuvwxyz'.ToCharArray() |
Get-Random -Count $LowerLetter

'§$%&?=#*+-'.ToCharArray() |
Get-Random -Count $Special

} | Sort-Object -Property { Get-Random }
$characters -join ''

下一步,将文件夹路径添加到 PowerShell 的命令搜索路径,例如运行这段代码:

1
PS> $env:path += ";c:\myPSCommands"

现在您可以想普通命令一样运行存储在文件夹中的任意脚本。如果脚本的开始处有 param() 块,那么支持传入参数。当您按示例操作后,就可以得到一个名为 New-Password 的命令,用来生成复杂密码,以及通过参数帮您组合密码:

1
2
PS> New-Password -CapitalLetter 2 -Numeric 1 -LowerLetter 8 -Special 2
yx+nKfph?M8rw