PowerShell 技能连载 - 删除别名

在 PowerShell 中创建新的别名很常见。但是如果您做错了什么,要怎么办?

PS C:\> Set-Alias -Name ping -Value notepad

PS C:\> ping 127.0.0.1

当创建了一个别名之后,并没有 cmdlet 可以移除它。您必须得关闭 PowerShell 并打开一个新的 PowerShell 会话来“忘记”掉自定义的别名。

或者,您可以利用 alias: 虚拟驱动器,并且像移除文件一样移除别名:

PS C:\> del alias:ping

PS C:\> ping 127.0.0.1

Pinging 127.0.0.1 with 32 bytes of data:
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128

Ping statistics for 127.0.0.1:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms

PS C:\>

PowerShell 技能连载 - 快速创建编码的 PowerShell 命令

当在 PowerShell 控制台之外执行 PowerShell 代码时,您需要传递代码给 powershell.exe。要确保您的代码不与特殊字符冲突,命令可以编码后传给 powershell.exe。

一个最简单的将纯文本命令行转换为编码后的命令的方法如下:

PS C:\> cmd /c echo powershell { Get-Service | Where-Object Status -eq Running }
powershell -encodedCommand IABHAGUAdAAtAFMAZQByAHYAaQBjAGUAIAB8ACAAVwBoAGUAcgBlAC0ATwBiAGoAZQ
BjAHQAIABTAHQAYQB0AHUAcwAgAC0AZQBxACAAUgB1AG4AbgBpAG4AZwAgAA== -inputFormat xml -outputFormat
 xml
PS C:\>

Here you’d find out that you can run the Get-Service | Where-Object statement as an encoded command like this:
然后可以以这样的方式执行编码后的 Get-Service | Where-Object 语句。

powershell.exe -encodedCommand
IABHAGUAdAAtAFMAZQByAHYAaQBjAGUAIAB8ACAAVwBoAGUAcgBlAC0ATwBiAGoAZQBjAHQAIABTAHQAYQB0AHUAcwAgAC0AZQBxACAAUgB1AG4AbgBpAG4AZwAgAA==

当您在 cmd.exe(或 PowerShell 控制台中)运行这段语句时,您能够得到所有运行中的服务。只需要移除 -inputFormat-outputFormat 参数,并且移除所有换行符。编码后的命令是一个长长的字符串。

PowerShell 技能连载 - 为变量增加 ValidateRange

如果您希望为一个变量增加一个合法数值的范围,您可以向该变量添加一个 ValidateRange 属性,很像函数参数的工作方式。唯一的区别在,它手工作用于您期望的变量上:

$test = 1
$variable = Get-Variable test
$validateRange = New-Object -TypeName System.Management.Automation.ValidateRangeAttribute(1,100)
$variable.Attributes.Add($validateRange)
$test = 10
$test = 100
$test = 1000

变量 $test 现在只允许 1 到 100 的数值。当您试图赋一个该范围之外的值时,会得到一个异常。

PS C:\> $test =  101
The variable cannot be  validated because the value 101 is not a valid value for the test
variable.
At line:1 char:1
+ $test = 101
+ ~~~~~~~~~~~
    +  CategoryInfo          : MetadataError:  (:) [], ValidationMetadataException
    +  FullyQualifiedErrorId : ValidateSetFailure

PowerShell 技能连载 - 为什么 $MaximumHistoryCount 容量有限

如果您想增加最大命令历史的容量,您可能会遇到这样的限制:

PS C:\> $MaximumHistoryCount  = 100000

The variable cannot be  validated because the value 100000 is not a valid value for the Maximum
HistoryCount variable.

这里并没有提示合法的范围是多少。有意思的地方是这个变量的合法范围保存在哪。答案是:您可以查询这个变量的 ValidateRange 属性:

$variable = Get-Variable MaximumHistoryCount
$variable.Attributes
$variable.Attributes.MinRange
$variable.Attributes.MaxRange

但您遇到一个变量在原始数据类型之外有数值限制,您可能需要检查变量的属性来确认其中是否有验证器属性。

PowerShell 技能连载 - 复制命令历史

您可以将整个命令历史拷贝到剪贴板中:

(Get-History).CommandLine | clip.exe

该技术使用 PowerShell 3.0 带来的自动展开技术。若要在 PowerShell 2.0 中使用它,您需要像这样手工展开属性:

Get-History | Select-Object -ExpandProperty commandline | clip.exe

要只拷贝最后五条命令,只需要为 Get-History 命令加上 -Count 参数即可:

(Get-History -Count 5).CommandLine | clip.exe

PowerShell 技能连载 - 增加历史缓存

当您在 PowerShell 会话中工作一段时间以后,命令历史可能十分有用。每个会话存储了您输入的所有命令,您可以按上下键浏览已输入的命令。

您甚至可以这样搜索历史缓存:

PS C:\> #obje

键入一个注释符(# 号),然后跟上您所能回忆起的命令关键字,然后按下 TAB 键,每按一次 TAB 将会显示命令历史中匹配的一条命令(如果没有匹配成功,将不会显示)。

要限制命令历史的大小,请使用 $MaximumHistoryCount 变量。缺省值是 4096。

PS C:\> $MaximumHistoryCount
4096

PS C:\> $MaximumHistoryCount = 32KB-1

PS C:\> $MaximumHistoryCount
32767

PS C:\>

历史缓存最大允许的容量是 32KB-1。

PowerShell 技能连载 - 获取最后启动时间

在 PowerShell 3.0 以上版本中,可以很容易地用 Get-CimInstance 从 WMI 中获取真实的 DateTime 类型信息。这段代码将告诉您系统上次启动的时间:

#requires -Version 3
(Get-CimInstance -ClassName Win32_OperatingSystem).LastBootUpTime

在 PowerShell 2.0 中,您只能使用 Get-WmiObject,它是以 WMI 格式反馈数据的:

(Get-WmiObject -Class Win32_OperatingSystem).LastBootUpTime

这里,您必须手工转换 WMI 格式:

$object = Get-WmiObject -Class Win32_OperatingSystem
$lastboot = $object.LastBootUpTime
$object.ConvertToDateTime($lastboot)

ConvertToDateTime() 转换函数实际上是一个附加的方法。在这个场景背后,是一个静态方法实现了以上工作:

$object = Get-WmiObject -Class Win32_OperatingSystem
$lastboot = $object.LastBootUpTime
[System.Management.ManagementDateTimeConverter]::ToDateTime($lastboot)

PowerShell 技能连载 - 复制对象

在前一个技能中我们演示了 PowerShell 是通过引用存储对象的。如果您想创建一个浮板,您可能需要手工复制对象的所有属性。

以下是一个简单的克隆对象的方法:

$object1 = @{Name='Weltner'; ID=12 }
$object2 = @{Name='Frank'; ID=99 }


$a = $object1, $object2

# clone entire object by serializing it back and forth:
$b = $a | ConvertTo-Json -Depth 99 | ConvertFrom-Json

$b[0].Name = 'changed'
$b[0].Name
$a[0].Name

不过,请注意序列化的过程可能会改变复制的对象类型。

PS C:\> $a[0].GetType().FullName
System.Collections.Hashtable

PS C:\> $b[0].GetType().FullName
System.Management.Automation.PSCustomObject

PS C:\>

PowerShell 技能连载 - 复制数组(第 2 部分)

在前一个技能中我们解释了如何用 Clone() 方法安全地“克隆”一个数组。这将把一个数组的内容复制到一个新的数组。

然而,如果数组的元素是对象(不是数字或字符串等原始数据类型),数组存储了这些对象的内存地址,所以克隆方法虽然创建了一个新的数组,但是新的数组仍然引用了相同对象。请看:

$object1 = @{Name='Weltner'; ID=12 }
$object2 = @{Name='Frank'; ID=99 }


$a = $object1, $object2
$b = $a.Clone()
$b[0].Name = 'changed'
$b[0].Name
$a[0].Name

虽然您克隆了数组 $a,但是新的数组 $b 仍然引用了相同的对象,对对象的更改会同时影响两个数组。只有对数组内容的更改是独立的:

$object1 = @{Name='Weltner'; ID=12 }
$object2 = @{Name='Frank'; ID=99 }


$a = $object1, $object2
$b = $a.Clone()
$b[0] = 'deleted'
$b[0]
$a[0]

PowerShell 技能连载 - 复制数组(第 1 部分)

当您复制变量内容时,您也可以只拷贝“引用”(内存地址),而不是内容。请看这个例子:

$a = 1..10
$b = $a
$b[0] = 'changed'
$b[0]
$a[0]

虽然您改变了 $b,但 $a 也跟着改变。两个变量都引用了相同的内存地址,所以两者具有相同的内容。

要创建一个数组的全新拷贝,您需要先对它进行克隆:

$a = 1..10
$b = $a.Clone()
$b[0] = 'changed'
$b[0]
$a[0]