PowerShell 技能连载 - 获取免费的速查表
有两个很棒的 PowerShell 速查表版本。一个是由 powershellmagazine.com 创建的一系列速查表。它刚刚升级,现在覆盖了 PowerShell 4.0 并包括了一个期望状态配置(DSC)的速查表。您可以从 Microsoft 下载它:
http://www.microsoft.com/en-us/download/details.aspx?id=42554
另外一个是我们的每月技巧文摘,地址如下:
有两个很棒的 PowerShell 速查表版本。一个是由 powershellmagazine.com 创建的一系列速查表。它刚刚升级,现在覆盖了 PowerShell 4.0 并包括了一个期望状态配置(DSC)的速查表。您可以从 Microsoft 下载它:
http://www.microsoft.com/en-us/download/details.aspx?id=42554
另外一个是我们的每月技巧文摘,地址如下:
期望状态配置(DSC)是 PowerShell 4.0 中的一个新特性。通过 DSC,您可以编写简单的配置脚本并且将它们应用到本地或远程的机器上。以下是一个供您入门的示例脚本:
Configuration MyConfig
{
# Parameters are optional
param ($MachineName)
# A Configuration block can have one or more Node blocks
Node $MachineName
{
Registry RegistryExample
{
Ensure = 'Present' # You can also set Ensure to "Absent"
Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\ExampleKey'
ValueName ='TestValue'
ValueData ='TestData'
}
}
}
MyConfig -MachineName $env:computername -OutputPath c:\dsc
Start-DscConfiguration -Path c:\dsc -Wait
配置项“MyConfig”使用了“Registry”资源来确保指定的注册表项存在。您可以在 DSC 脚本中使用更多的资源,例如增加(或删除)本地用户或文件,解压一个 MSI 包或 ZIP 文件,或启动/停止一个服务等等。
运行该配置只会创建一个 MOF 文件。要应用该 MOF 文件,请使用 Start-DSCConfiguration
cmdlet。请使用 -Wait
来等待配置生效。否则,该配置将会在后台以任务的方式完成。
PowerShell 的函数可以模拟一个真实二进制 cmdlet 的所有特性,但是 PowerShell 函数是 PowerShell 明文的代码,每个人都可以看到它的内容。
如果您是一个开发者并且有兴趣创开发二进制 cmdlet,以下是一个快速的入门。该入门演示如何用纯 PowerShell 创建并编译真正的 cmdlet:
# C# definition for cmdlet
$code = @'
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Management.Automation;
namespace CustomCmdlet
{
[Cmdlet("Get", "Magic", SupportsTransactions = false)]
public class test : PSCmdlet
{
private int _Age;
[Alias(new string[]
{
"HowOld", "YourAge"
}), Parameter(Position = 0,ValueFromPipeline = true)]
public int Age
{
get { return _Age; }
set { _Age = value; }
}
private string _Name;
[Parameter(Position = 1)]
public string Name
{
get { return _Name; }
set { _Name = value; }
}
protected override void BeginProcessing()
{
this.WriteObject("Good morning...");
base.BeginProcessing();
}
protected override void ProcessRecord()
{
this.WriteObject("Your name is " + Name + " and your age is " + Age);
base.ProcessRecord();
}
protected override void EndProcessing()
{
this.WriteObject("That's it for now.");
base.EndProcessing();
}
}
}
'@
# compile C# code to DLL
# use a timestamp to create unique file names
# while testing, when a DLL was imported before, it is in use until PowerShell closes
# so to do repeated tests, use different DLL file names
$datetime = Get-Date -Format yyyyMMddHHmmssffff
$DLLPath = "$env:temp\myCmdlet($datetime).dll"
Add-Type -TypeDefinition $code -OutputAssembly $DLLPath
# import a module
Import-Module -Name $DLLPath -Verbose
现在您可以可以使用新创建的 Get-Magic
cmdlet。它包含了一个 cmdlet 能实现的所有特性,包括参数、参数别名,甚至支持管道:
请注意例子中主要的 PowerShell 代码只是为了创建并编译 DLL。当 DLL 已经存在时,您需要的只是这行代码(例如,在分发的产品中):
Import-Module -Name $DLLPath
要开发复杂的二进制 cmdlet,您可能更希望在 C# 开发环境,例如 Visual Studio 中工作。您所需的只是添加 PowerShell 程序集的引用。PowerShell 程序集的路径可以用这行代码方便地获取到:
它将会把 PowerShell 程序集的路径输出到您的剪贴板中。
请注意只是编译 C# 代码并不会为您的知识资产带来更多的保护,因为它可以被反编译。所以不要用这种方式来“保护”秘密的信息,比如说密码。通过二进制 cmdlet,您可以有机会使用专业的防拷贝软件以及混淆器。额外的保护层并没有纯 PowerShell 代码的版本。
当您需要为一个文件添加一个新的 NTFS 存取规则,或禁用继承并添加新的规则,以下是一个示例脚本,演示这个技巧并且为您提供一个模板。
这个脚本创建一个测试文件,然后以当前用户的身份定义一个新的存取规则。这个规则包含读取和写入权限。这个新规则被添加到已存在的安全描述符中。另外,将禁用继承。
# create a sample file to apply security rules to
$Path = "$env:temp\examplefile.txt"
$null = New-Item -Path $Path -ItemType File -ErrorAction SilentlyContinue
# use current user or replace with another user name
$username = "$env:USERDOMAIN\$env:USERNAME"
# define the new access rights
$colRights = [System.Security.AccessControl.FileSystemRights]'Read, Write'
$InheritanceFlag = [System.Security.AccessControl.InheritanceFlags]::None
$PropagationFlag = [System.Security.AccessControl.PropagationFlags]::None
$objType =[System.Security.AccessControl.AccessControlType]::Allow
$objUser = New-Object System.Security.Principal.NTAccount($username)
# create new access control entry
$objACE = New-Object System.Security.AccessControl.FileSystemAccessRule `
($objUser, $colRights, $InheritanceFlag, $PropagationFlag, $objType)
# get existing access control list for a file or folder
$objACL = Get-Acl -Path $Path
# add rule
$objACL.AddAccessRule($objACE)
# disable inheritance (if needed)
$objACL.SetAccessRuleProtection($true, $false)
# apply changed access control list to file
Set-Acl -Path $Path -AclObject $objACL
# show file in the File Explorer
explorer.exe "/SELECT,$Path"
执行完成之后,该脚本在文件管理器中打开测试文件,并选中它。您可以右键单击该文件并选择 属性
> 安全
来查看新的设置。
要查看有哪些存取权限可用,请在 ISE 编辑器中键入以下这行:
这将自动打开上下文菜单并列出所有可用的设置。
某些时候,文本文件中的文字内容需要由其它命令来读取和处理。通常,您需要用 Get-Content
命令来读取文本文件内容,然后将结果传递给其它命令。但这有可能会失败。
以下是注意点:请牢记 Get-Content
总是返回一个文本行的数组,而不是单个文本行。所以当使用一个接收字符串而不是接收一系列文本行(字符串数组)的命令时,您需要将这些文本行转换为文本。
从 PowerShell 3.0 开始,Get-Content
拥有一个新的开关变量 -Raw
。它不仅提升了读取大量文本的速度,而且一次性返回原始文本文件的整个内容,而不是将其分割成文本行。
PS> $info = Get-Content $env:windir\windowsupdate.log
PS> $info -is [Array]
True
PS> $info = Get-Content $env:windir\windowsupdate.log -Raw
PS> $info -is [Array]
False
如果您已经有文本数组并且希望将它们转换为一个单一的文本,请使用 Out-String
:
PS> $info = 'One', 'Two', 'Three'
PS> $info -is [Array]
True
PS> $all = $info | Out-String
PS> $all -is [Array]
False
在 Powershell ISE 中,有一个模糊的缺陷,可能会导致调试器死锁。受影响的 PowerShell 版本有 3.0 和 4.0。
以下是一段测试脚本:
$test = @"
Some lines
of text
"@
$test
在 ISE 编辑器中将这段代码保存为脚本,然后在第一行中设置一个断点:单击第一行的任何地方,然后按下 F9
键。该行将会变成红色。
当您启动脚本时,调试器将会在断点处停下,然后您可以按 F10
键单步跟踪代码。这可以正常工作。
现在,在变量定义之前加入一些空格:
$test = @"
Some lines
of text
"@
$test
当您现在调用调试器的时候,它将会死锁,并且 ISE 不会恢复。您还可以保存未保存的脚本,但您再也无法停止 ISE 的运行空间。
这个缺陷在对通过 here string 定义的脚本变量缩进的时候暴露出来。
有些时候,我们需要批量重命名对象的属性来更好地创建报表。例如,假设您获取了进程对象,你需要您可能需要以新的的列名来创建报表。
以下是一个称为 Rename-Property
的过滤器,可以用于重命名任何属性。在例子中,生成了一个进程列表,然后重命名一些属性:
filter Rename-Property ([Hashtable]$PropertyMapping)
{
Foreach ($key in $PropertyMapping.Keys)
{
$_ = $_ | Add-Member -MemberType AliasProperty -Name $PropertyMapping.$key -Value $key -PassThru
}
$_
}
$newProps = @{
Company = 'Manufacturer'
Description = 'Purpose'
MainWindowTitle = 'TitlebarText'
}
# get raw data
Get-Process |
# add alias properties as specified in $newProps
Rename-Property $newProps |
# select the properties you want to display
# can be original properties and/or newly added alias properties
Select-Object -Property Name, Manufacturer, Purpose, TitlebarText
Rename-Property
自动加入了 $newProps
中指定的所有属性。结果对象中含有名为“Manufacturer”、“Purpose”和“TitlebarText”的新属性。您可以接着使用 Select-Object
来选择您想在报表中包含的属性。您可以从原先存在的属性中选择,也可以从新增加的别名属性中选择。
所以本质上上,属性并没有被改名(技术上不可能实现)。实际上,该过滤器以新的名字添加了别名属性,并指向原先的属性。
当您发送信息到 Microsoft Excel 中时,它将被 .NET 内置的 ToString()
方法转化为文本。这个方法通常并不能正确地转化数组或非基本数据类型。
以下是一个例子演示这个问题。它创建了一个您系统事件日志中 10 个最近错误事件的报表:
$Path = "$env:temp\$(Get-Random).csv"
Get-EventLog -LogName System -EntryType Error -Newest 10 |
Select-Object EventID, MachineName, Data, Message, Source, ReplacementStrings, InstanceId, TimeGenerated |
Export-Csv -Path $Path -Encoding UTF8 -NoTypeInformation -UseCulture
Invoke-Item -Path $Path
“Data”字段和“ReplacementStrings”无法使用。由于两个属性都包含数组,自动转换的结果只是简单显示数据的类型的名称。这是在从对象数据中创建 Excel 报表的常见现象。
要改进报表,您可以显式地使用 PowerShell 引擎将对象转化为文本,然后将多行文本转换为单行文本。
您可以对每个看起来不正确的字段运用这个方法。以下是上一个例子的解决方案,它能够改进 Message、Data 和 ReplacementStrings 字段的显示:
$Path = "$env:temp\$(Get-Random).csv"
Get-EventLog -LogName System -EntryType Error -Newest 10 |
Select-Object EventID, MachineName, Data, Message, Source, ReplacementStrings, InstanceId, TimeGenerated |
ForEach-Object {
$_.Message = ($_.Message | Out-String -Stream) -join ' '
$_.Data = ($_.Data | Out-String -Stream) -join ', '
$_.ReplacementStrings = ($_.ReplacementStrings | Out-String -Stream) -join ', '
$_
} |
Export-Csv -Path $Path -Encoding UTF8 -NoTypeInformation -UseCulture
Invoke-Item -Path $Path
现在所有字段都显示了正确的结果。请注意有问题的属性首先通过管道发送到 Out-String
命令(用 PowerShell 内部的机制将数据转换为有意义的文本),然后用 -join
将信息连接成单行文本。
还请注意“Message”属性是如何处理的。虽然这个属性看起来没问题,但是它实际上有可能是多行文本。多行信息在 Excel 中将只显示第一行,并以“…”结尾。我们将这些行通过空格连接之后,Excel 便可以显示完整信息了。
PowerShell 对象可以很容易地通过 Microsoft Excel 打开。只需要将对象导出成 CSV,然后通过关联的应用程序打开 CSV 文件(如果装了 Excel,那么将用 Excel 打开)。
以下代码创建一个当前运行的进程报告,并用 Excel 打开:
$Path = "$env:temp\$(Get-Random).csv"
$originalProperties = 'Name', 'Id', 'Company', 'Description', 'WindowTitle'
Get-Process |
Select-Object -Property $originalProperties |
Export-Csv -Path $Path -Encoding UTF8 -NoTypeInformation -UseCulture
Invoke-Item -Path $Path
请注意 -UseCulture
如何根据您的区域设置自动选择正确的分隔符。
缺省情况下,从 JSON 创建的对象使用 String 作为数据的类型:
$json = @"
{
"Name": "Weltner",
"ID" : "123"
}
"@
$info = ConvertFrom-Json -InputObject $json
$info.Name
$info.ID
但是,JSON 不支持数据类型,虽然 JSON 数据类型并不等价于 .NET 数据类型。请注意在以下代码中,“ID”被定义成“数字”型,并且它被赋予一个数值型的值,而不需要用双引号引起来。
$json = @"
{
"Name": "Weltner",
number: "ID" : 123
}
"@
$info = ConvertFrom-Json -InputObject $json
$info.Name
$info.ID
然而,当使用 ConvertFrom-Json
时,我们发现 PowerShell 并没有关心数据类型定义。它总是将值转换为 String 数据。