PowerShell 技能连载 - 将信息转换为区域性特定的文本

如果您希望将信息转换为指定的区域标准,那么可以轻松地用 ToString() 方法和合适的目标信息。

这个例子将短浅的日期和时间转换为法国的格式:

1
2
3
4
$date = Get-Date
$frenchCulture = New-Object -TypeName CultureInfo("fr")
$dateString = $date.ToString($frenchCulture)
$dateString

当您选择了泰国区域,您会注意到一个完全不同的年份,因为泰国使用不同的日历模型:

1
2
3
4
$date = Get-Date
$thaiCulture = New-Object -TypeName CultureInfo("th")
$dateString = $date.ToString($thaiCulture)
$dateString

当您选择了一个不同的区域,Windows 也会相应地翻译并显示月份和日期名称:

1
2
3
4
$date = Get-Date
$chineseCulture = New-Object -TypeName CultureInfo("zh")
$dateString = $date.ToString('dddd dd.MMMM yyyy',$chineseCulture)
$dateString

结果类似这样:

星期三 01.十一月 2017

PowerShell 技能连载 - 转换区域性特定的信息

假设您以文本的形式收到了数字或日期等数据。当信息转为文本的时候,您会遇到区域性特定的格式:不同区域的小数点和日期时间可能会不同。

以下是一个如何解析数据的简单例子,假设您知道它的来源区域信息:

1
2
3
4
5
6
# number in German format
$info = '1,2'

# convert to real value
$culture = New-Object -TypeName CultureInfo("de")
$number = [Double]::Parse($info,$culture)

每一个目标类型有一个 Parse() 方法,所以如果您收到一个日期和/或时间信息,您可以将它如此简单地转换它。这个例子输入的是以法国标准格式化的日期和时间,并返回一个真正的 DateTime 对象:

1
2
3
4
5
6
7
# date and time in French format:
$info = '01/11/2017 16:28:45'

# convert to real value
$culture = New-Object -TypeName CultureInfo("fr")
$date = [DateTime]::Parse($info,$culture)
$date

PowerShell 技能连载 - 理解文本转换

有许多方法能将对象转换为文本。如果您在某些情况下弄糊涂了,这篇文章能帮您快速搞清。请看:

有三个基础的将对象转换为文本的方法。

1
2
3
4
5
6
7
$a = 4.5
# simple conversion
$a.ToString()
# slightly better conversion
"$a"
# richest conversion
$a | Out-String

对于简单对象,三个方法的结果不会有差别。然而,对于更复杂的对象,结果差异可能很大。请看一下数组:

1
2
3
4
5
6
7
$a = 1,2,3

$a.ToString()

"$a"

$a | Out-String

并且看看复杂对象如何转换为文本:

1
2
3
4
5
6
7
$a = Get-Process

$a.ToString()

"$a"

$a | Out-String

PowerShell 技能连载 - 强制允许 PowerShell 脚本执行

执行策略 (Execution Policy) 可以禁止脚本执行。它被设计成一个用户的首选项,所以您总是可以改变有关的执行策略。不过,在一些环境下,组策略可以强制改变设置,并且禁止运行脚本。

在这种情况下,您可以考虑重置内部的 PowerShell 授权管理。将它替换为一个缺省的实例以后,您可以忽略之前的执行策略设置,执行 PowerShell 脚本:

1
$context = $executioncontext.gettype().getfield('_context','nonpublic,instance').getvalue($executioncontext); $field = $context.gettype().getfield('_authorizationManager','nonpublic,instance'); $field.setvalue($context,(New-Object management.automation.authorizationmanager 'Microsoft.PowerShell'))

请注意,这不是一个安全问题。执行策略的控制权在用户。这并不是一个安全边界。

PowerShell 技能连载 - 比较字符串列表

在前一个例子中我们使用 HashSet 来对比数字列表,并查找哪些元素在两个列表中都出现,或只在一个列表中出现。

字符串列表也可以做相同的事。假设您有两个名字清单,并且希望知道哪些名字在两个名单中都出现,或只在一个名单中出现,请试试以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$set1 = New-Object System.Collections.Generic.HashSet[string] (,[string[]]@('Harry','Mary','Terri'))
$set2 = New-Object System.Collections.Generic.HashSet[string] (,[string[]]@('Tom','Tim','Terri','Tobias'))

"Original Sets:"
"$set1"
"$set2"

# in both
$copy = New-Object System.Collections.Generic.HashSet[string] $set1
$copy.IntersectWith($set2)
"In Both"
"$copy"

# combine
$copy = New-Object System.Collections.Generic.HashSet[string] $set1
$copy.UnionWith($set2)
"All Combined"
"$copy"

# exclusive
$copy = New-Object System.Collections.Generic.HashSet[string] $set1
$copy.ExceptWith($set2)
"Exclusive in Set 1"
"$copy"

# exclusive either side
$copy = New-Object System.Collections.Generic.HashSet[string] $set1
$copy.SymmetricExceptWith($set2)
"Exclusive in both (no duplicates)"
"$copy"

以下是执行结果:

Original Sets:
Harry Mary Terri
Tom Tim Terri Tobias
In Both
Terri
All Combined
Harry Mary Terri Tom Tim Tobias
Exclusive in Set 1
Harry Mary
Exclusive in both (no duplicates)
Harry Mary Tobias Tom Tim

PowerShell 技能连载 - 比较数字列表

一个脚本常常需要对比两个列表是否相等,或者某个列表是否缺失了某个元素。这种情况下,您可以使用 HashSet,而不用手动写代码。请看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$set1 = New-Object System.Collections.Generic.HashSet[int32] (,[int[]]@(1,2,5,7,9,12))
$set2 = New-Object System.Collections.Generic.HashSet[int32] (,[int[]]@(1,2,5,12,111))

"Original Sets:"
"$set1"
"$set2"

# in both
$copy = New-Object 'System.Collections.Generic.HashSet[int32]' $set1
$copy.IntersectWith($set2)
"In Both"
"$copy"

# combine
$copy = New-Object 'System.Collections.Generic.HashSet[int32]' $set1
$copy.UnionWith($set2)
"All Combined"
"$copy"

# exclusive
$copy = New-Object 'System.Collections.Generic.HashSet[int32]' $set1
$copy.ExceptWith($set2)
"Exclusive in Set 1"
"$copy"

# exclusive either side
$copy = New-Object 'System.Collections.Generic.HashSet[int32]' $set1
$copy.SymmetricExceptWith($set2)
"Exclusive in both (no duplicates)"
"$copy"

这个例子演示了如何创建两个初始的集合:$set1$set2

要计算一个列表和另一个列表的差别,首先创建一个列表的工作拷贝,然后可以运用各种比较方法,将工作拷贝和另一个列表做比较。

结果可以在工作拷贝中直接查看。这是以上代码的执行结果:

Original Sets:
1 2 5 7 9 12
1 2 5 12 111
In Both
1 2 5 12
All Combined
1 2 5 7 9 12 111
Exclusive in Set 1
7 9
Exclusive in both (no duplicates)
7 9 111

PowerShell 技能连载 - 为对象高效添加标记

有时候您会见到用 Select-Object 向已有对象增加信息的脚本,类似以下代码:

1
2
3
4
5
6
Get-Process |
Select-Object -Property *, Sender|
ForEach-Object {
$_.Sender = $env:COMPUTERNAME
$_
}

这段代码可以工作,但是 Select-Object 创建了一个全新的对象拷贝,所以这种方法速度比较慢并且改变了对象的类型。因为这个原因,您会注意到 PowerrShell 不再像正常的那样以表格的方式输出。

Add-Member 是首选的增加额外信息到已有对象的 cmdlet,因为它不会拷贝对象并且不会改变对象类型。请比较输出结果:

1
2
Get-Process |
Add-Member -MemberType NoteProperty -Name Sender -Value $env:COMPUTERNAME -PassThru

对象类型没有改变,并且 PowerShell 继续使用进程查看缺省的输出布局。这是因为新的 “Sender” 属性初始是不可见的。不过它事实上存在:

1
2
3
Get-Process |
Add-Member -MemberType NoteProperty -Name Sender -Value $env:COMPUTERNAME -PassThru |
Select-Object -Property Name, Id, Sender

PowerShell 技能连载 - 高效创建新的对象

多数时候,大部分新对象都是静态数据,以属性的方式表示。一个特别有效的创建新的包含新属性的对象是将哈希表转换为对象——我们早些时候转换过:

1
2
3
4
5
$conf = [PSCustomObject]@{
Name = 'Tobias'
Conf = 'psconf.eu'
Url = 'http://psconf.eu'
}

输出的结果是一个简单的对象,没有任何特别的方法:

1
2
3
4
5
PS C:\> $conf

Name Conf Url
---- ---- ---
Tobias psconf.eu http://psconf.eu

要增加方法,请在随后使用 Add-Member 修饰该对象。这行代码增加一个新的 Register() 方法:

1
2
$object |
Add-Member -MemberType ScriptMethod -Name Register -Value { Start-Process -FilePath $this.url }

请注意脚本块代码如何通过 Register() 方法来存取 $this$this 变量代表对象自身,所以即便您晚些时候才决定改变 “Url“ 属性,Register() 方法将仍然可以工作。

当您运行 Register() 方法,该对象打开 “Url“ 属性指定的 URL:

1
PS C:\> $conf.Register()

PowerShell 技能连载 - 在 PowerShell ISE 中添加单行注释

在前一个技能中,我们学习了针对 PowerShell ISE 的命令扩展。以下是另一个例子,增加 CTRL+K 键盘快捷键,在选中的每一行尾部添加注释 (#)。

1
2
3
4
5
6
7
8
function Invoke-Comment
{
$file = $psise.CurrentFile
$comment = ($file.Editor.SelectedText -split '\n' | ForEach-Object { "#$_" }) -join "`n"
$file.Editor.InsertText($comment)
}

$psise.CurrentPowerShellTab.AddOnsMenu.Submenus.Add('Comment Out', { Invoke-Comment }, 'CTRL+K')

PowerShell 技能连载 - 在 PowerShell ISE 中切换注释

PowerShell ISE 暴露了一些可扩展的组件。例如,如果您希望按下 CTRL+K 切换选中文本的注释状态,请试试这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Toggle-Comment
{
$file = $psise.CurrentFile
$text = $file.Editor.SelectedText
if ($text.StartsWith("<#")) {
$comment = $text.Substring(3).TrimEnd("#>")
}
else
{
$comment = "<#" + $text + "#>"
}
$file.Editor.InsertText($comment)
}

$psise.CurrentPowerShellTab.AddOnsMenu.Submenus.Add('Toggle Comment', { Toggle-Comment }, 'CTRL+K')

它基本上是使用 $psise 来操作 ISE 对象模型,然后安装一个快捷键为 CTRL+K 的新的菜单命令来调用 Toggle-Comment 函数。

在位于 $profile 的用户配置文件脚本中(这个路径可能还不存在)增加这段代码,PowerShell ISE 每次启动的时候就会自动运行这段代码。