PowerShell 技能连载 - 确定个人年龄

您是如何基于生日确定一个人的年龄?您可以将 Get-Date 命令返回的当前时间减去生日事件,但是结果并不包含年数:

1
2
3
4
5
6
7
#requires -Version 1.0

$birthday = Get-Date -Date '1978-12-09'
$today = Get-Date
$timedifference = $today - $birthday

$timedifference

以下是结果:

1
2
3
4
5
6
7
8
9
10
11
Days              : 13905
Hours : 16
Minutes : 34
Seconds : 58
Milliseconds : 575
Ticks : 12014516985758198
TotalDays : 13905.6909557387
TotalHours : 333736.582937728
TotalMinutes : 20024194.9762637
TotalSeconds : 1201451698.57582
TotalMilliseconds : 1201451698575.82

要计算年数,请取 “ticks” 的数值(衡量时间最小单位),并且转换为 datetime 类型,然后取年数并减一:

1
2
3
4
5
6
7
8
#requires -Version 1.0
$birthdayString = '1978-12-09'
$birthday = Get-Date -Date $birthdayString
$today = Get-Date
$timedifference = $today - $birthday
$ticks = $timedifference.Ticks
$age = (New-Object DateTime -ArgumentList $ticks).Year -1
"Born on $birthdayString = $age Years old (at time of printing)"

这是计算结果的样子:

1
Born on 1978-12-09 = 38 Years old (at time of printing)

PowerShell 技能连载 - 加速 New-Object Synthesizer

New-Object 创建新的对象实例,在之前的“语音之周”中,您已经见到了如何创建一个语音合成器对象,并且将文本转换为语音:

1
2
3
Add-Type -AssemblyName System.Speech
$speak = New-Object -TypeName System.Speech.Synthesis.SpeechSynthesizer
$speak.Speak('Hello I am PowerShell!')

创建对象的方法是类似的,所以如果换成一个不同的类,例如 System.Net.NetworkInformation.Ping,就可以 ping 某个 IP 地址或主机名:

1
2
3
4
5
$ping = New-Object -TypeName System.Net.NetworkInformation.Ping
$timeout = 1000
$result = $ping.Send('powershellmagazine.com', $timeout)

$result

在 PowerShell 5.0 或以上版本,有另一种方法来代替 New-Object,而且用起来更快:使用任意类型暴露的 New() 静态方法。您可以像这样重写以上的例子:

1
2
3
Add-Type -AssemblyName System.Speech
$speak = [System.Speech.Synthesis.SpeechSynthesizer]::New()
$speak.Speak('Hello I am PowerShell!')

类似地:

1
2
3
4
5
$ping = [System.Net.NetworkInformation.Ping]::New()
$timeout = 1000
$result = $ping.Send('powershellmagazine.com', $timeout)

$result

或者,可以精简:

1
[System.Net.NetworkInformation.Ping]::New().Send('powershellmagazine.com', 1000)

请注意:一旦您使用 New() 来代替 New-Object,您的代码需要 PowerShell 5.0 以上版本。

PowerShell 技能连载 - 语音之周:使用语音合成器高级选项

.NET 语音引擎不止可以接受文本输入。如果您使用 SpeakSsm(),您可以使用 XML 来切换语言、速度,以及其它文本到语音转换的参数。

以下例子需要同时安装了英语和德语的语音。如果您没有安装德语语音,请使当地修改脚本中的语言 ID。以下是查找系统中可用的语言 ID 的方法:

1
2
3
4
5
6
7
8
PS C:\> Add-Type -AssemblyName System.Speech

PS C:\> $speak.GetInstalledVoices() | Select-Object -ExpandProperty VoiceInfo | Select-Object -ExpandProperty Culture | Sort-Object -Unique

LCID Name DisplayName
---- ---- -----------
1031 de-DE German (Germany)
1033 en-US English (United States)

以下是完整的例子:

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
#requires -Version 2.0
Add-Type -AssemblyName System.Speech
$speak = New-Object System.Speech.Synthesis.SpeechSynthesizer
$ssml = '
<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis"
xml:lang="en-US">
<voice xml:lang="en-US">
<prosody rate="1">
<p>I can speak English!</p>
</prosody>
</voice>
<voice xml:lang="de-DE">
<prosody rate="1">
<p>und ich kann auch deutsch sprechen!</p>
</prosody>
</voice>
<voice xml:lang="en-US">
<prosody rate="0">
<p>...and sometimes I get really tired.</p>
</prosody>
</voice>
</speak>
'

$speak.SpeakSsml($ssml)

PowerShell 技能连载 - 语音之周:记录语音到文件合成器

内置的 Microsoft 文本到语音引擎可以将音频文件保存到文件。通过这种方式,您可以自动生成 WAV 文件。以下是一个例子:它在您的桌面上创建一个新的 “clickme.wav” 文件,当您打开这个文件时,将会听到语音文本:

1
2
3
4
5
6
7
8
9
10
#requires -Version 2.0
$Path = "$home\Desktop\clickme.wav"

Add-Type -AssemblyName System.Speech
$speak = New-Object System.Speech.Synthesis.SpeechSynthesizer
$speak.SetOutputToWaveFile($Path)
$speak.Speak('Hello I am PowerShell!')
$speak.SetOutputToDefaultAudioDevice()

Invoke-Item -Path $Path

PowerShell 技能连载 - 语音之周:更改讲述人的语音

在前一个技能中我们演示了如何使用语音转换器来念出文本。以下是查找您系统中安装的语言的方法:

1
2
3
4
5
6
#requires -Version 2.0
Add-Type -AssemblyName System.Speech
$speak = New-Object System.Speech.Synthesis.SpeechSynthesizer
$speak.GetInstalledVoices() |
Select-Object -ExpandProperty VoiceInfo |
Select-Object -Property Culture, Name, Gender, Age

结果类似如下:

1
2
3
4
5
Culture Name                    Gender   Age
------- ---- ------ ---
en-US Microsoft Zira Desktop Female Adult
en-US Microsoft David Desktop Male Adult
de-DE Microsoft Hedda Desktop Female Adult

用这行代码可以返回缺省的语音:

1
$speak.Voice

假设您的系统安装了多个语音,以下是选择一个不同语音的方法。只需要传入您想使用的语音名字。这个例子在德文 Windows 10 系统上使用德语语音引擎:

1
2
3
4
5
#requires -Version 2.0
Add-Type -AssemblyName System.speech
$speak = New-Object System.Speech.Synthesis.SpeechSynthesizer
$speak.SelectVoice('Microsoft Hedda Desktop')
$speak.Speak('Jetzt spreche ich deutsch.')

PowerShell 技能连载 - 语音之周:使用语音讲述人

当您将 “System.Speech“ 程序集添加到 PowerShell 中后,就可以使用新增的 “SpeechSynthesizer“ 类将文字转成语音:

1
2
3
Add-Type -AssemblyName System.Speech
$speak = New-Object System.Speech.Synthesis.SpeechSynthesizer
$speak.Speak('Hello I am PowerShell!')

请注意语音讲述人用的是您系统的缺省语音。您的讲述人缺省情况下可能说的不是英文。我们将在接下来的技能当中介绍如何使用不同的语音。

PowerShell 技能连载 - 增加和删除反斜杠

我们处理路径时,常常需要使路径“标准化”。例如确保所有的路径都以反斜杠结尾。一种尝试如下:

1
2
3
4
5
6
$path = 'c:\temp'
if ($path -notmatch '\\$')
{
$path += '\'
}
$path

这段代码用正则表达式来查找一段文本尾部的反斜杠。如果不存在,则添加一个反斜杠。

如果您想删除路径尾部的反斜杠,您可以直接使用 -replace 运算符:

1
2
$path = 'c:\temp\' -replace '\\$'
$path

PowerShell 技能连载 - 检查整数的位数

有些时候您可能会需要检查一个整数的位数,例如验证用户的输入。以下是一个非常简单的使用正则表达式的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# check the number of digits in an integer
$integer = 5721567

# is it between 4 and 6 digits?
$is4to6 = $integer -match '^\d{4,6}$'

# is it exactly 7 digits?
$is7 = $integer -match '^\d{7}$'

# is it at least 4 digits?
$isatleast4 = $integer -match '^\d{4,}$'

"4-6 digits? $is4to6"
"exactly 7 digits? $is7"
"at least 4 digits? $isatleast4"

这个例子演示了如何检查是否是恰好的位数,或者位数处于某个范围。请注意 “^” 代表表达式的起始,”$” 代表表达式的结尾。”\d” 表示一位数字,大括号确定位数。

PowerShell 技能连载 - 在资源管理器中打开 PowerShell

一个在文件资源管理器中快速启动 PowerShell 的办法是导航到您数据的文件夹,然后点击导航条。这时导航面包屑控件变成了文件夹路径。将它改为 “powershell”,并按下回车键。

这时会打开 PowerShell,并且当前文件夹会设置为您导航到的文件夹。

不过,当前路径下有一个名为 “powershell” 的子文件夹时,这个技巧会失效。在这个例子中,文件资源管理器只会导航到该目录中。

PowerShell 技能连载 - 隐藏进度条

有些 cmdlet 和脚本使用进度条来指示进度。如您在前一个技能中所学的,进度条可能会导致延迟,所以如果您如果不想指示进度,您可能会希望隐藏进度条。以下是实现方法:

以下代码从互联网下载一张图片。Invoke-WebRequest 处理耗时的操作并且在下载时显示一个进度条:

1
2
3
4
5
6
7
8
#requires -Version 3.0


$path = "$home\Pictures\psconf15.jpg"
$url = 'http://www.powertheshell.com/wp-content/uploads/groupWPK2015.jpg'
Invoke-WebRequest -Uri $url -OutFile $path

Invoke-Item -Path $path

如果您不想显示进度条,请使用 $ProgressPreference 变量,临时隐藏进度条。请注意代码是如何用大括号包裹起来,并且用 ”&“ 号执行的。通过这种方法,当代码执行完毕后,所有在大括号中改变的变量都会被忽略,所以您不需要将 $ProgressPreference 变量重置为原来的值。

1
2
3
4
5
6
7
8
9
10
11
#requires -Version 3.0


& {
$ProgressPreference = 'SilentlyContinue'

$path = "$home\Pictures\psconf15.jpg"
$url = 'http://www.powertheshell.com/wp-content/uploads/groupWPK2015.jpg'
Invoke-WebRequest -Uri $url -OutFile $path
}
Invoke-Item -Path $path