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

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

一种扩展 PowerShell 命令的简单方法是使用脚本。要将一段脚本转换为命令,请选择一个文件夹并将 PowerShell 脚本存储在该文件夹中。脚本的名字将会转化为命令名。

例如,将以下脚本以 “New-Password” 名字保存在一个文件夹中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$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 包含您在搜索命令中保存脚本的文件夹。假设您将脚本保存在名为 “c:\myPsCommands” 文件夹下。然后运行以下代码将会将该文件夹添加到命令搜索路径中:

1
$env:path += ";c:\myPsCommands"

一旦您做了这个调整,就可以输入命令名 “New-Password“ 轻松运行您的脚本。本质上该脚本名称转化为一个可执行的命令名。

PowerShell 技能连载 - 导出 Edge 的 Cookie

如果您希望查找或者导出 Edge 浏览器存储的网站 cookie,PowerShell 可以帮您导出以上信息。cookie 列表实际上存储在一个 SQLite 数据库的 “Cookies” 表中。

从 PowerShellGallery.com 安装了 “ReallySimpleDatabase” 免费模块之后,连接和读取数据库十分容易:

1
2
3
4
5
6
7
8
9
10
11
12
13
#requires -Modules ReallySimpleDatabase

<#
make sure you install the required module before you run this script:
Install-Module -Name ReallySimpleDatabase -Scope CurrentUser
#>

$path = "$env:LOCALAPPDATA\Microsoft\Edge\User Data\Default\Network\Cookies"

$db = Get-Database -Path $PATH

$db.InvokeSql('select * from cookies') |
Select-Object host_key, name

PowerShell 技能连载 - 研究 PowerShell 命令结果

HTML 是一种简单的格式化输出报告的方法。在这个三部曲系列中,我们首先演示如何生成 HTML 报告,然后展示一种简单的方法将 HTML 报告转为 PDF 文档。

一个简单的研究命令返回结果的方法是使用 Select-Object 显示第一个(随机的)返回结果的所有属性。以下是一个例子:

1
Get-Service | Select-Object -Property * -First 1

通过这种方法,您可以获得一个返回结果,它的所有属性都可见,并且您可以看见这些属性中的实际数值来更好地评估要使用哪些属性。

另一个研究的方法是使用 Get-Member 从更偏技术/定义的角度查看所有可用的属性:

···powershell
Get-Service | Get-Member -MemberType *property


现在,您可以查看所有返回的数据类型,以及每个定义为 "`{get;}`"(只读)或 "`{get;set;}`"(读写)的属性。
<!--本文国际来源:[Investigating PowerShell Command Results](https://blog.idera.com/database-tools/powershell/powertips/investigating-powershell-command-results/)-->

PowerShell 技能连载 - 通过 SNMP 查询高级的打印机

许多网络打印机支持使用 SNMP 查询设备信息,例如序列号、状态和纸仓中纸张的大小,以及错误信息。

在 Windows 系统中,操作系统通过 PowerShell 已经提供所有 SNMP 查询所需的组件。您所需要知道的只是打印机的 IP 地址。当然,请确保它已开机并支持 SNMP。

以下是测试脚本:

1
2
3
4
5
6
7
8
9
10
11
12
# define your printer network IP here:
$Printer_IP = '192.168.2.200'

# connect to printer:
$SNMP = New-Object -ComObject olePrn.OleSNMP
$SNMP.Open($Printer_IP,'public')

# get device description
$SNMP.Get(".1.3.6.1.2.1.25.3.2.1.3.1")
# get device serial number
$SNMP.Get(".1.3.6.1.2.1.43.5.1.1.17.1")
$SNMP.Close()

这段代码非常短小。

更有挑战性的是:除了设备描述和序列号之外您能找到哪些信息?如何知道您可以查询的剩余信息片段的 ID 号。下面是一个最常用的 ID 列表。不过,并非所有打印机都支持所有的 ID:

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
#region list of IDs that you can ask your printer:
# (not all IDs will work with all printers)
$OID_RAW_DATA = ".1.3.6.1.2.1.43.18.1.1"
$OID_CONSOLE_DATA = ".1.3.6.1.2.1.43.16"
$OID_CONTACT = ".1.3.6.1.2.1.1.4.0"
$OID_LOCATION = ".1.3.6.1.2.1.1.6.0"
$OID_SERIAL_NUMBER = ".1.3.6.1.2.1.43.5.1.1.17.1"
$OID_SYSTEM_DESCRIPTION = ".1.3.6.1.2.1.1.1.0"
$OID_DEVICE_DESCRIPTION = ".1.3.6.1.2.1.25.3.2.1.3.1"
$OID_DEVICE_STATE = ".1.3.6.1.2.1.25.3.2.1.5.1"
$OID_DEVICE_ERRORS = ".1.3.6.1.2.1.25.3.2.1.6.1"
$OID_UPTIME = ".1.3.6.1.2.1.1.3.0"
$OID_MEMORY_SIZE = ".1.3.6.1.2.1.25.2.2.0"
$OID_PAGE_COUNT = ".1.3.6.1.2.1.43.10.2.1.4.1.1"
$OID_HARDWARE_ADDRESS = ".1.3.6.1.2.1.2.2.1.6.1"
$OID_TRAY_1_NAME = ".1.3.6.1.2.1.43.8.2.1.13.1.1"
$OID_TRAY_1_CAPACITY = ".1.3.6.1.2.1.43.8.2.1.9.1.1"
$OID_TRAY_1_LEVEL = ".1.3.6.1.2.1.43.8.2.1.10.1.1"
$OID_TRAY_2_NAME = ".1.3.6.1.2.1.43.8.2.1.13.1.2"
$OID_TRAY_2_CAPACITY = ".1.3.6.1.2.1.43.8.2.1.9.1.2"
$OID_TRAY_2_LEVEL = ".1.3.6.1.2.1.43.8.2.1.10.1.2"
$OID_TRAY_3_NAME = ".1.3.6.1.2.1.43.8.2.1.13.1.3"
$OID_TRAY_3_CAPACITY = ".1.3.6.1.2.1.43.8.2.1.9.1.3"
$OID_TRAY_3_LEVEL = ".1.3.6.1.2.1.43.8.2.1.10.1.3"
$OID_TRAY_4_NAME = ".1.3.6.1.2.1.43.8.2.1.13.1.4"
$OID_TRAY_4_CAPACITY = ".1.3.6.1.2.1.43.8.2.1.9.1.4"
$OID_TRAY_4_LEVEL = ".1.3.6.1.2.1.43.8.2.1.10.1.4"
$OID_BLACK_TONER_CARTRIDGE_NAME = ".1.3.6.1.2.1.43.11.1.1.6.1.1"
$OID_BLACK_TONER_CARTRIDGE_CAPACITY = ".1.3.6.1.2.1.43.11.1.1.8.1.1"
$OID_BLACK_TONER_CARTRIDGE_LEVEL = ".1.3.6.1.2.1.43.11.1.1.9.1.1"
$OID_CYAN_TONER_CARTRIDGE_NAME = ".1.3.6.1.2.1.43.11.1.1.6.1.2"
$OID_CYAN_TONER_CARTRIDGE_CAPACITY = ".1.3.6.1.2.1.43.11.1.1.8.1.2"
$OID_CYAN_TONER_CARTRIDGE_LEVEL = ".1.3.6.1.2.1.43.11.1.1.9.1.2"
$OID_MAGENTA_TONER_CARTRIDGE_NAME = ".1.3.6.1.2.1.43.11.1.1.6.1.3"
$OID_MAGENTA_TONER_CARTRIDGE_CAPACITY = ".1.3.6.1.2.1.43.11.1.1.8.1.3"
$OID_MAGENTA_TONER_CARTRIDGE_LEVEL = ".1.3.6.1.2.1.43.11.1.1.9.1.3"
$OID_YELLOW_TONER_CARTRIDGE_NAME = ".1.3.6.1.2.1.43.11.1.1.6.1.4"
$OID_YELLOW_TONER_CARTRIDGE_CAPACITY = ".1.3.6.1.2.1.43.11.1.1.8.1.4"
$OID_YELLOW_TONER_CARTRIDGE_LEVEL = ".1.3.6.1.2.1.43.11.1.1.9.1.4"
$OID_WASTE_TONER_BOX_NAME = ".1.3.6.1.2.1.43.11.1.1.6.1.5"
$OID_WASTE_TONER_BOX_CAPACITY = ".1.3.6.1.2.1.43.11.1.1.8.1.5"
$OID_WASTE_TONER_BOX_LEVEL = ".1.3.6.1.2.1.43.11.1.1.9.1.5"
$OID_BELT_UNIT_NAME = ".1.3.6.1.2.1.43.11.1.1.6.1.6"
$OID_BELT_UNIT_CAPACITY = ".1.3.6.1.2.1.43.11.1.1.8.1.6"
$OID_BELT_UNIT_LEVEL = ".1.3.6.1.2.1.43.11.1.1.9.1.6"
$OID_BLACK_DRUM_UNIT_NAME = ".1.3.6.1.2.1.43.11.1.1.6.1.7"
$OID_BLACK_DRUM_UNIT_CAPACITY = ".1.3.6.1.2.1.43.11.1.1.8.1.7"
$OID_BLACK_DRUM_UNIT_LEVEL = ".1.3.6.1.2.1.43.11.1.1.9.1.7"
$OID_CYAN_DRUM_UNIT_NAME = ".1.3.6.1.2.1.43.11.1.1.6.1.8"
$OID_CYAN_DRUM_UNIT_CAPACITY = ".1.3.6.1.2.1.43.11.1.1.8.1.8"
$OID_CYAN_DRUM_UNIT_LEVEL = ".1.3.6.1.2.1.43.11.1.1.9.1.8"
$OID_MAGENTA_DRUM_UNIT_NAME = ".1.3.6.1.2.1.43.11.1.1.6.1.9"
$OID_MAGENTA_DRUM_UNIT_CAPACITY = ".1.3.6.1.2.1.43.11.1.1.8.1.9"
$OID_MAGENTA_DRUM_UNIT_LEVEL = ".1.3.6.1.2.1.43.11.1.1.9.1.9"
$OID_YELLOW_DRUM_UNIT_NAME = ".1.3.6.1.2.1.43.11.1.1.6.1.10"
$OID_YELLOW_DRUM_UNIT_CAPACITY = ".1.3.6.1.2.1.43.11.1.1.8.1.10"
$OID_YELLOW_DRUM_UNIT_LEVEL = ".1.3.6.1.2.1.43.11.1.1.9.1.10"
#endregion