PowerShell 技能连载 - 不带动词运行 Cmdlet

这是从 PowerShell 1.0 开始就具有的一个特性:调用动词为 “get” 的 cmdlet 可以省略动词。所以调用 “Get-Service“ 时您可以仅执行 “Service“调用 “Get-Date“ 时可以仅执行 “Date“。

以下不是别名,甚至 PowerShell 引擎并不知道为什么它能工作。请试试这些代码:

1
2
PS> Date
PS> Get-Command Date

使用这个快捷方式的前提是没有冲突的命令或语法元素。这也是为什么您可以运行 “Get-Process“,但不能运行 “Process“ 的原因:”Process“ 是 PowerShell 语言中的一个保留关键字。

PowerShell 技能连载 - 限制文本的长度(第二部分)

以下是确保一段文本不超过指定长度的另一种策略。和前一个技能不同的是,当文本长度小于最大长度时,这段代码不会补齐空格:

1
2
3
4
$text = 'this'
$MaxLength = 10
$CutOff = [Math]::Min($MaxLength, $text.Length)
$text.Substring(0,$CutOff)

关键点在 Min() 函数,它决定了两个值中小的哪个。

PowerShell 技能连载 - 限制文本的长度(第一部分)

如果您想将一个文本的长度限制在某一个长度,以下是一个简单的方法:

1
2
3
4
$text = 'this is a long text'
$MaxLength = 10

$text.PadRight($MaxLength).Substring(0,$MaxLength)

这段代码首先对文本填充,以防它比最大长度还短,然后使用 Substring() 裁剪掉多余的文本。

PowerShell 技能连载 - 查找所有含桌面的配置文件

这一行代码能够列出所有本地用户配置文件中的桌面——请确保以管理员身份运行这行代码才能查看其他人的配置文件:

1
Resolve-Path -Path C:\users\*\Desktop -ErrorAction SilentlyContinue

如果您只想获得配置文件中包含 “Desktop” 文件夹的用户名,请用以下代码:

1
2
3
4
Resolve-Path -Path C:\users\*\Desktop -ErrorAction SilentlyContinue |
ForEach-Object {
$_.Path.Split('\')[-2]
}

这段代码获取路径并用反斜杠将它们分割,创建一个路径元素的数组。下标 -2 是指倒数第二个元素,即用户名。

PowerShell 技能连载 - Where-Object 和 .Where()

从 PowerShell 4 开始,当您不想使用管道的时候,可以使用 Where()ForEach() 方法来代替 Where-ObjectForEach-Object

所以如果您已经将所有数据加载到一个变量中,那么非流式操作会更高效:

1
2
3
4
5
6
$Services = Get-Service

# streaming
$Services | Where-Object { $_.Status -eq 'Running' }
# non-streaming
$Services.Where{ $_.Status -eq 'Running' }

要节约资源,最有效地方法仍然是使用流式管道,而不是用变量:

1
Get-Service | Where-Object { $_.Status -eq 'Running' }

请注意 Where-Object.Where() 使用不同的数组类型,所以它们的输出技术上是不同的:

1
2
3
4
5
PS C:\> (1..19 |  Where-Object { $_ -gt 10 }).GetType().FullName
System.Object[]

PS C:\> ((1..19).Where{ $_ -gt 10 }).GetType().FullName
System.Collections.ObjectModel.Collection`1[[System.Management.Automation.PSObject, System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]

PowerShell 技能连载 - 快速创建对象数组

以下是一个用内置的 CSV 处理器生成对象数组的代码实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$csv = @'
PC,Date
PC82012,2017-02-28
PC82038,2017-02-28
PC83073,2017-02-28
PC84004,2017-02-28
PC84009,2017-02-28
PC84015,2017-02-28
PC90435,2017-02-28
'@

$data = $csv | ConvertFrom-Csv

$data
$data | Out-GridView

如果一个脚本需要一个静态的服务器、连接数据或其他信息的列表,这种方式会很有用。

PowerShell 技能连载 - 探索类型加速器

PowerShell 使用了大量所谓类型加速器来简化过长的 .NET 类型名。例如 “System.DirectoryServices.DirectoryEntry” 可以简化为 “ADSI”。

当您需要查询一个类型的完整名称时,您可以获取到实际的完整 .NET 类型名:

1
2
3
4
PS C:\> [ADSI].FullName
System.DirectoryServices.DirectoryEntry

PS C:\>

以下代码在 PowerShell 中输出所有的内置 .NET 类型加速器:

1
2
[PSObject].Assembly.GetType("System.Management.Automation.TypeAccelerators")::get |
Out-GridView

除了显式的类型加速器之外,还有一个 PowerShell 内置的规则:在 System 命名空间中的类型加速器可以省略命名空间。所以以下的表达完全一致:

1
2
3
4
5
6
7
8
PS C:\> [int].FullName
System.Int32

PS C:\> [System.Int32].FullName
System.Int32

PS C:\> [Int32].FullName
System.Int32

PowerShell 技能连载 - 危险的临时文件!

内部的系统功能往往十分有用,但请确保真正了解它们的功能。

一个特别常见的系统方法叫做 GetTempFileName() ,能够创建临时文件名。而当您进一步观察的时候,您会发现它不仅创建临时文件名,而且还创建了临时文件:

1
2
$file = [System.IO.Path]::GetTempFileName()
Test-Path -Path $file

所以如果在脚本中只是使用这个方法来创建临时文件名的话,会留下一大堆孤立的文件。

PowerShell 技能连载 - 探索对象

在 PowerShell 中,一切都是用对象描述。以下是一个检查任意对象并将它的成员以文本的方式复制到剪贴板的单行代码:

1
2
3
4
5
"Hello" |
Get-Member |
Format-Table -AutoSize -Wrap |
Out-String -Width 150 |
clip.exe

只需要将 “Hello” 替换成任何变量或命令,然后看看复制了什么到剪贴板中。您可以将信息粘贴到文本编辑器或文字处理器中,并将它打印出来或转成 PDF 备用。

PowerShell 技能连载 - 管理比特标志位(第四部分)

在 PowerShell 5 中,对枚举的新支持特性使得处理比特位比您在前面的 PowerShell 技能中看到的简单得多。现在设置或清除比特位不再需要冗长的逻辑操作符。

我们先定义一个枚举类型,这样更好管理十进制数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#requires -Version 5

[Flags()]
enum GardenPartyItems
{
Chair = 0
Table = 1
Barbecue = 2
Fridge = 4
Candle = 8
Knife = 16
}

$decimal = 11
[GardenPartyItems]$flags = $decimal
$flags

现在,十进制数的比特位可以很容易地转化为 GardenPartyItem 的列表:

1
2
3
4
PS C:\>  [GardenPartyItems]11
Table, Barbecue, Candle

PS C:\>

注意:将十进制数转换为枚举型时,请确保枚举型中定义了所有的比特。如果十进制数太大,包含枚举型之外的比特时,转换会失败。

要增加一个新的标志位,请试试以下的代码:

1
2
3
4
5
6
7
8
9
PS C:\> $flags
Table, Barbecue, Candle

PS C:\> $flags += [GardenPartyItems]::Knife

PS C:\> $flags
Table, Barbecue, Candle, Knife

PS C:\>

要移除一个标志位,请试试以下代码:

1
2
3
4
5
6
7
8
9
PS C:\> $flags
Table, Barbecue, Candle, Knife

PS C:\> $flags -= [GardenPartyItems]::Candle

PS C:\> $flags
Table, Barbecue, Knife

PS C:\>

然而,实际上并没有看起来这么简单。当移除一个已有的标志位,没有问题。但移除一个没有置位的标志位,会把比特值搞乱:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PS C:\> $flags
Table, Barbecue, Candle

PS C:\> $flags -= [GardenPartyItems]::Candle

PS C:\> $flags
Table, Barbecue

PS C:\> $flags -= [GardenPartyItems]::Candle

PS C:\> $flags
-5

PS C:\>

所以 PowerShell 在自动处理二进制算法方面明显还不够智能。要安全地使用该功能,您还是要用二进制操作符。要移除标志位,请使用 -band-bnot

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PS C:\> $flags
Table, Barbecue, Candle

PS C:\> $flags = $flags -band -bnot [GardenPartyItems]::Candle

PS C:\> $flags
Table, Barbecue

PS C:\> $flags = $flags -band -bnot [GardenPartyItems]::Candle

PS C:\> $flags
Table, Barbecue

PS C:\>

要设置标志位,请使用 -bor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PS C:\> $flags
Table, Barbecue, Candle

PS C:\> $flags = $flags -bor [GardenPartyItems]::Knife

PS C:\> $flags
Table, Barbecue, Candle, Knife

PS C:\> $flags = $flags -bor [GardenPartyItems]::Knife

PS C:\> $flags
Table, Barbecue, Candle, Knife

PS C:\>

在所有这些操作中,实际上是在操作一个十进制数:

1
2
PS C:\> [Int]$flags
19

相当棒,对吧?