PowerShell 技能连载 - 使用 PowerShell 参数验证器

PowerShell 的函数参数支持 ValidateScript 属性,可以关联到一段 PowerShell 代码。当该参数接收到一个值时,该代码将会被调用,并且返回 $true$false。如果该代码返回 $false 则该参数被拒绝。

以下是一个仅接受 Windows 文件夹中存在的文件的文件名的例子:

1
2
3
4
5
6
7
8
9
10
11
12
function Get-File
{
param
(
[Parameter(Mandatory)]
[ValidateScript({ Test-Path -Path "$env:windir\$_" })]
[string]
$File
)

"$File exists in your Windows folder."
}

以下是使用效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
PS C:\> Get-File  -File explorer.exe
explorer.exe exists in your Windows folder.

PS C:\> Get-File -File something.exe
Get-File : Cannot validate argument on parameter 'File'. The " Test-Path -Path "$env:windir\$_" " validation script for the argument with value "something.exe" did not return a result of True. Determine why the validation script failed, and then try the command again.
At line:1 char:16
+ Get-File -File something.exe
+ ~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Get-File], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Get-File

PS C:\> Get-File -File memory.dmp
memory.dmp exists in your Windows folder.

PowerShell 技能连载 - 按区域转换数据

当转换数据(将它转换为不同的数据类型)时,PowerShell 支持两种不同的方式方式。

以下是一个例子:

1
2
[DateTime]'12.1.2017'
'12.1.2017' -as [DateTime]

两行代码都将一个字符串转为一个 DateTime 对象。第一行代码代表强制转换。它可能成功也有可能失败,并且它总是使用语言中性的格式(US 格式),所以它应为一个 月-日-年 格式。

第二行代表“尝试转换”:该转换要么成功要么静默地返回 $null。该转换遵循当前的区域设置,所以如果您在一个德文系统众运行这段代码,这段文字被解释成 日-月-年 格式。

PowerShell 技能连载 - 调用一个脚本块

可以通过调用操作符,例如 “&“、”.“ 或调用 Invoke() 方法调用在一个脚本块中的代码。

一个区别是当有多于一个结果时的输出:调用操作符返回一个扁平的对象数组,而 Invoke() 返回一个集合:

1
2
3
4
5
6
7
$code = { Get-Process }

$result1 = & $code
$result2 = $code.Invoke()

$result1.GetType().FullName
$result2.GetType().FullName

通过 Invoke() 方法返回的集合拥有额外的方法,例如 RemoveAt()Insert(),它们能够帮您修改结果数据,能高效地插入或删除元素。

您可以手动将一个 cmdlet 的返回值手动转为一个 ArrayList:

1
$arrayList = [Collections.ArrayList]@(Get-Process)

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