PowerShell 技能连载 - 处理数据(第 1 部分)
这是关于 PowerShell 函数如何通过管道或参数接受数据的三个技巧中的第一个。
在第一部分中,函数实时处理输入的信息。这消耗最少的内存并且快速提供结果:
1 | #requires -Version 2 |
请注意如何通过参数调用函数:
1 | PS C:\> Process-Data -Object 1 |
您也可以通过管道传送信息:
1 | PS C:\> 1..4 | Process-Data |
这是关于 PowerShell 函数如何通过管道或参数接受数据的三个技巧中的第一个。
在第一部分中,函数实时处理输入的信息。这消耗最少的内存并且快速提供结果:
1 | #requires -Version 2 |
请注意如何通过参数调用函数:
1 | PS C:\> Process-Data -Object 1 |
您也可以通过管道传送信息:
1 | PS C:\> 1..4 | Process-Data |
在第 1 部分中我们演示了一个 PowerShell 函数如何同时从参数和管道获取输入,并且实时处理它。这是最有效的方法并节省内存开销。
然而,有时需要先收集所有数据,待所有数据收集完成以后,一次性处理所有数据。以下是一个收集所有收到的数据并等所有数据都到齐以后才开始处理的例子:
#requires -Version 2
function Collect-Data
{
param
(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
[Object]
[AllowEmptyString()]
$Object
)
begin
{
$bucket = New-Object System.Collections.ArrayList
}
process
{
$null = $bucket.Add($Object)
}
end
{
$count = $bucket.Count
Write-Host "Received $count objects." -ForegroundColor Yellow
$bucket | Out-String
}
}
请注意 Collect-Data
如何既从参数又从管道获取信息:
PS C:\> Collect-Data -Object 1,2,3
Received 3 objects.
1
2
3
PS C:\> 1..3 | Collect-Data
Received 3 objects.
1
2
3
有两件事值得一提:千万不要用一个纯数组来收集信息。而是使用一个 ArraryList
对象,因为它添加新的元素比较快。并且避免将 $input
用于类似用途的自动变量。$input
只能用于管道输入并且忽略提交到参数的值。
PowerShell 不仅支持文件系统,您可以将当前路径设置为别的 provider(用 Set-Location
命令)。以下是一个始终返回当前文件系统,无论当前激活的是那个 provider 的技巧:
1 | PS C:\> cd hkcu:\ |
从 PowerShell 3.0 开始,您可以通过哈希表创建预先初始化好的对象。只需要添加您希望预先初始化的属性,然后将哈希表转换为期望的类型。
以下是一个实际的例子:
1 | #requires -Version 3 |
当您运行这段代码时,PowerShell 创建一个新的 System.Speech
对象并且预先初始化了 rate 和 volume 的值。当您用 SpeakAsync()
方法将文本输出到语音时,文本会被很慢地念出来。Rate
的取值在 -10 到 10 之间。
PowerShell 3.0 增加了Get-WmiObject
的另一个选择:Get-CimInstance
,它工作起来十分相似但可以从内部 的 WMI 服务中获取信息:
1 | PS C:\> Get-WmiObject -Class Win32_BIOS |
虽然 Get-WmiObject
仍然存在,但 Get-CimInstance
绝对是未来的选择。这个 Cmdlet 支持 WMI 类的智能提示(在 PowerShell ISE 中),并且返回的数据可读性更好:例如日期是以人类可读的日期格式返回,而 Get-WmiObject
显示 WMI 内部原始的日期格式。
最重要的区别是它们远程工作的方法。Get-WmiObject
使用的是旧的 DCOM 协议,而 Get-CimInstance
缺省使用的是新的 WSMan
协议,不过它是灵活的,可以根据需要退回 DCOM 协议。
以下示例函数通过 Get-CimInstance
远程获取 BIOS 信息。该函数缺省采用 DCOM,通过 -Protocol
参数您可以选择希望的通信协议:
1 | #requires -Version 3 |
PowerShell 提供七种不同的流,可以用来输出信息。流可以帮助筛选信息,因为流可以不输出。实际上一些流默认是不输出的。以下是一个名为 Test-Stream
的示例函数。它运行后会将信息发送给所有七种流。
请注意:Write-Information
是 PowerShell 5.0 新加入的。如果您想在早期的 PowerShell 版本中运行,请移除调用 Write-Information
的语句!
1 | function Test-Stream |
这应该是您运行 Test-Stream
能看到的结果:
1 | PS C:\> Test-Stream |
如您所见,echo
和 Write-Output
工作起来效果相同,而且实际上它们确实是相同的(因为echo
是 Write-Output
的别名)。它们定义了一个或多个返回值。它们可以赋值给一个变量。同理,这个规则适用于函数留下的未赋值的变量:它们也被送到 Write-Output
流中。
Write-Host
直接将输出送到控制台,所以它一定可见。这个 Cmdlet 只能用于向用户传递信息的场景。
其他的流是静默的。要查看其它流的输出,您首先需要打开它们:
1 | $VerbosePreference = 'Continue' |
当打开之后,Test-Stream
将输出这样的信息:
1 | PS C:\> Test-Stream |
要恢复缺省值,请复位 preference 变量:
1 | $VerbosePreference = 'SilentlyContinue' |
如果在函数中加入了通用参数,您就可以在调用函数时使用使用 -Verbose
和 -Debug
开关。Test-CommonParameter
演示了如何添加通用参数支持。
1 | function Test-CommonParameter |
当运行 Test-CommonParameter
时,您将立即明白 -Verbose
和 Debug
通用参数是如何工作的:它们只是改变了本地 preference 变量:
1 | PS C:\> Test-CommonParameter |
在前一个技能中我们演示了如何将任何图片转换为 Base64 编码的字符串。今天,我们将演示如何加载 Base64 编码的图片并显示它。
这个窗口显示官方会议 logo 并且不需要独立的图片文件。该图像已经嵌入到脚本中并且直接从内存中加载。
在脚本头部的嵌入图片十分大。最重要的部分是在 Base64 编码字符串之后的 Convert-Base64Bitmap2Picture
函数。它接受 Base64 编码的文本并且返回一个 bitmap 对象。这个对象可以设为任何 WPF image 控件的源。
1 | Add-Type -AssemblyName PresentationFramework |
如果您的脚本需要图标或图片等资源,您不需要另外发布这些资源。它们可以用 Base64 编码并且以纯文本的方式加到您的脚本中。
这个例子演示了如何将一个 JPG 图片转换为 Base64 编码的字符串:
1 | function Convert-JPG2Base64 |
Convert-JPG2Base64
函数接受一个 JPG 图片路径作为参数并且返回 Base64 编码后的图片。在这个例子中,我们使用 Windows 文件夹中的第一个 JPG 墙纸。请确保您的 Windows 文件夹中包含图片,或者把 JPG 图片的文件夹改为您想要的文件夹。
返回的文本可以嵌入一段脚本中。而且,返回的 Base64 文本可能会非常大,由图片的尺寸和质量决定。
明天,我们将演示如何将 Base64 编码后的图片加载到内存中,并在自己的 WPF 窗口中显示。
以下是将文本用 Base64 编码的简单方法:
1 | #requires -Version 1 |
结果字符串看起来大概如下:
SABlAGwAbABvACAAVwBvAHIAbABkACEA
文本编码可以用于简易的混淆文本,或是保护文本防止不小心被错误地格式化。例如 PowerShell.exe 可运行 Base64 编码过的命令。以下是一个例子(请打开您机器的声音):
1 | powershell.exe -EncodedCommand ZgBvAHIAKAAkAHgAIAA9ACAAMQAwADAAMAA7ACAAJAB4ACAALQBsAHQAIAAxADIAMAAwADAAOwAgACQAeAArAD0AMQAwADAAMAApACAAewAgAFsAUwB5AHMAdABlAG0ALgBDAG8AbgBzAG8AbABlAF0AOgA6AEIAZQBlAHAAKAAkAHgALAAgADMAMAAwACkAOwAgACIAJAB4ACAASAB6ACIAfQA= |
要解码一个 Base64 字符串,您可以使用以下代码。
1 | #requires -Version 1 |
您也可以使用这段代码来解码上面那段编码后的命令,看看它做了什么。只需要用编码过的命令替换掉 $test
。
从 Windows 8 和 Server 2012 开始,有一个 Cmdlet 可以在多个配置中启用客户端防火墙:
1 | Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled True |
在之前的操作系统中,您需要使用依靠 netsh.exe:
1 | netsh advfirewall set allprofiles state on |