PowerShell 技能连载 - 在 PowerShell 控制台中使用颜色

从 PowerShell 5.1 开始,PowerShell 控制台支持 VT 转义序列,它可以用于对控制台文本定位和格式化。请注意它只对控制台有效,而对 PowerShell ISE 无效。另外还请注意您需要 Windows 10 或者类似 ConEmu 等模拟器。

要对一段文字着色,您在任何 PowerShell 版本中都可以使用 Write-Host 和它的 -ForegroundColor-BackgroundColor 属性:

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
foreach($color1 in (0..15))
{
foreach($color2 in (0..15))
{
Write-Host -ForegroundColor ([ConsoleColor]$color1) -BackgroundColor ([ConsoleColor]$color2) -Object "X" -NoNewline
}

Write-Host
}



XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX

通过使用 VT 转义序列,您可以使用范围更广的颜色。可以将前景色和背景色设为任意的 RGB 颜色。每个通道可以有 8 比特 (0-255):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Red/Green/Blue foreground
$r = 0
$g = 20
$b = 255

# Red/Green/Blue background
$rback = 255
$gback = 100
$bback = 0

$esc = [char]27

# compose escape sequence
"$esc[38;2;$r;$g;$b;48;2;$rback;$gback;${bback}mCOLORFUL TEXT$esc[0m"

以下代码创建所有可能的颜色,并且将它们显示在由 256 个字符组成的一行上,并且每一行叠加在前一行之上,这样可以获得一个渐变发光的效果:

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
$esc = [char]27
$setCursorTop = "$esc[0;0H"

foreach($rback in (0..255))
{
foreach($gback in (0..255))
{
foreach($bback in (0..255))
{

foreach($r in (0..255))
{
foreach($g in (0..255))
{
[System.Text.StringBuilder]$line = ""
foreach($b in (0..255))
{
$null = $line.Append("$esc[38;2;$r;$g;$b;48;2;$rback;$gback;${bback}mX$esc[0m")
}

$text = $line.ToString()
Write-Host "$setCursorTop$Text"
}

Write-Host
}
}
}
}

PowerShell 技能连载 - PowerShell 控制台光标定位

从 PowerShell 5.1 开始,PowerShell 控制台支持 VT 转义序列,它可以用于对控制台文本定位和格式化。请注意它只对控制台有效,而对 PowerShell ISE 无效。另外还请注意您需要 Windows 10 或者类似 ConEmu 等模拟器。

VT 转义序列可以将控制台光标设置到控制台窗口的任意位置。例如,要将光标设置到左上角,请使用以下代码:

1
2
3
4
$esc = [char]27
$setCursorTop = "$esc[0;0H"

Write-Host "${setCursorTop}This always appears in line 0 and column 0!"

当您运行这段代码时,文字总是定位在第 0 行 第 0 列。您可以使用这种技术来创建您自己的进度指示器——只需要记住:这一切只在控制台窗口中有效,在 PowerShell ISE 中无效。

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
function Show-CustomProgress
{
try
{
$esc = [char]27

# let the caret move to column (horizontal) pos 12
$column = 12
$resetHorizontalPos = "$esc[${column}G"
$gotoFirstColumn = "$esc[0G"

$hideCursor = "$esc[?25l"
$showCursor = "$esc[?25h"
$resetAll = "$esc[0m"

# write the template text
Write-Host "${hideCursor}Processing %." -NoNewline

1..100 | ForEach-Object {
# insert the current percentage
Write-Host "$resetHorizontalPos$_" -NoNewline
Start-Sleep -Milliseconds 100
}
}
finally
{
# reset display
Write-Host "${gotoFirstColumn}Done. $resetAll$showCursor"
}
}

运行这段代码后,执行 Show-CustomProgress 命令,您将会见到一个不断增长的自定义进度指示器。控制台隐藏了闪烁的光标提示。当进度指示器结束时,或者当按下 CTRL + C 时,进度指示器将会隐藏,

PowerShell 技能连载 - 在同一行输出日志信息

从 PowerShell 5.1 开始,PowerShell 控制台支持 VT 转义序列,它可以用于对控制台文本定位和格式化。请注意它只对控制台有效,而对 PowerShell ISE 无效。另外还请注意您需要 Windows 10 或者类似 ConEmu 等模拟器。

VT 转义序列可以将控制台光标设置到当前行的任意位置。通过这种方式,您可以方便地创建一个函数,输出状态或者日志信息到控制台。并且每条新信息覆盖之前的信息而不是增加新的行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Write-ConsoleMessage([string]$Message)
{
$esc = [char]27
$consoleWidth = [Console]::BufferWidth
$outputText = $Message.PadRight($consoleWidth)
$gotoFirstColumn = "$esc[0G"
Write-Host "$gotoFirstColumn$outputText" -NoNewline
}

function test
{
Write-ConsoleMessage -Message 'Starting...'
Start-Sleep -Seconds 1
Write-ConsoleMessage -Message 'Doing something!'
Start-Sleep -Seconds 1
Write-ConsoleMessage -Message 'OK.'
}

test

PowerShell 技能连载 - 为控制台输出加下划线

从 PowerShell 5.1 开始,PowerShell 控制台支持 VT 转义序列,它可以用于对控制台文本定位和格式化。请注意它只对控制台有效,而对 PowerShell ISE 无效。另外还请注意您需要 Windows 10 或者类似 ConEmu 等模拟器。

要实验这个功能,请在 PowerShell 控制台中运行以下代码:

1
2
$esc = [char]27
"$esc[4mOutput is now underlined!"

PowerShell 现在对所有的输出加下划线。输入文本并没有加下划线(由于 PSReadLine 处理所有输入文本的格式)。

要关闭格式化功能,请运行这段代码:

1
2
$esc = [char]27
"$esc[0mReset"

我们将会在未来的技能中介绍更多的控制台文字格式化技术。以下是 VT 转义序列的更深入介绍:https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences.

PowerShell 技能连载 - 安全地嵌入变量

当您在 PowerShell 中使用双引号时,您可以向字符串中增加变量,PowerShell 能自动将它们替换成它们的值——这并不是什么新鲜事:

1
2
3
$ID = 234

"Server $ID Rack12"

然而,PowerShell 自动判断一个变量的结束位置,所以当您希望在一个文本中插入一个不含空格的数字时,这种写法可能会失败:

1
2
3
$ID = 234

"Server$IDRack12"

如同语法高亮的信息,PowerShell 会将变量识别成 $IDRack12 因为它无法意识到变量名提前结束。

在这些情况下,只需要用大括号将变量名括起来即可:

1
2
3
$ID = 234

"Server${ID}Rack12"

PowerShell 技能连载 - 语音合成 – 使用不同的语音(第 4 部分)

Windows 10 自带优秀的文本转语音功能,以及不同的高品质语音。要查看哪些语音可用,请试试以下代码:

1
2
3
4
5
6
Add-Type -AssemblyName System.speech
$synthesizer = New-Object System.Speech.Synthesis.SpeechSynthesizer

$synthesizer.GetInstalledVoices().VoiceInfo |
Where-Object { $_.Name -notlike 'Microsoft Server*' } |
Select-Object -Property Name, Gender, Age, Culture

结果类似如下(根据您的 windows 版本、语言文化,和安装的组件可能有所不同):

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

要使用这些语音,请用 SelectVoice()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$sampleText = @{
[System.Globalization.CultureInfo]::GetCultureInfo("en-us") = "Hello, I am speaking English! I am "
[System.Globalization.CultureInfo]::GetCultureInfo("de-de") = "Halli Hallo, man spricht deutsch hier! Ich bin "
[System.Globalization.CultureInfo]::GetCultureInfo("es-es") = "Una cerveza por favor! Soy "
[System.Globalization.CultureInfo]::GetCultureInfo("fr-fr") = "Vive la france! Je suis "
[System.Globalization.CultureInfo]::GetCultureInfo("it-it") = "Il mio hovercraft è pieno di anguille! Lo sono "


}


Add-Type -AssemblyName System.speech
$synthesizer = New-Object System.Speech.Synthesis.SpeechSynthesizer

$synthesizer.GetInstalledVoices().VoiceInfo |
Where-Object { $_.Name -notlike 'Microsoft Server*' } |
Select-Object -Property Name, Gender, Age, Culture |
ForEach-Object {
$_
$synthesizer.SelectVoice($_.Name)
$synthesizer.Speak($sampleText[$_.Culture] + $_.Name)

}

哪些语音可用,依赖于系统已安装了哪些语言。以下链接解释了不同语言的 Windows 10 自带了哪些语音:

https://support.microsoft.com/en-us/help/22797/windows-10-narrator-tts-voices.
请注意 SeletVoice() 并不能使用所有已安装的语音。

PowerShell 技能连载 - 合成语音 – 使用语音合成标记语言 SSML(第 3 部分)

Windows 内置的文字转语音引擎可以输入纯文本,并且将它转换为语音,但它也可以通过“语音合成标记语言”来控制。通过这种方式,您可以对语音调优,控制音调,以及语言。

Windows 自带本地的语音引擎,所以最好控制一下采用的语言。否则,在德文系统上,您的英文文本发音听起来会很奇怪。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Add-Type -AssemblyName System.speech
$synthesizer = New-Object System.Speech.Synthesis.SpeechSynthesizer

$Text = '
<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>Normal pitch. </p>
<p><prosody pitch="x-high"> High Pitch. </prosody></p>
</prosody>
</voice>
</speak>
'
$synthesizer.SpeakSsml($Text)

根据已经安装的语音引擎,您现在甚至可以切换语言:

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
Add-Type -AssemblyName System.speech
$synthesizer = New-Object System.Speech.Synthesis.SpeechSynthesizer

$Text1 = '
<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>Normal pitch. </p>
<p><prosody pitch="x-high"> High Pitch. </prosody></p>
</prosody>
</voice>
</speak>
'

$text2 = '<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis"
xml:lang="en-US">
<voice xml:lang="de-de">
<prosody rate="1">
<p>Normale Tonhöhe. </p>
<p><prosody pitch="x-high"> Höhere Tonlage. </prosody></p>
</prosody>
</voice>
</speak>'

$synthesizer.SpeakSsml($Text1)
$synthesizer.SpeakSsml($Text2)

如果您想在文字中混合多种语言,您也可以使用传统的 COM 对象 Sapi.SpVoice。以下代码来自前一个技能:

1
2
3
4
5
6
7
8
9
$text = "<LANG LANGID=""409"">Your system will restart now!</LANG>
<LANG LANGID=""407""><PITCH MIDDLE = '2'>Oh nein, das geht nicht!</PITCH></LANG>
<LANG LANGID=""409"">I don't care baby</LANG>
<LANG LANGID=""407"">Ich rufe meinen Prinz! Herbert! Tu was!</LANG>
"

$speaker = New-Object -ComObject Sapi.SpVoice
$speaker.Rate = 0
$speaker.Speak($text)

PowerShell 技能连载 - 合成语音(第 2 部分)

在前一个技能中我们介绍了文字转语音引擎。这个引擎可以将文本转为一个 WAV 声音文件。这样我们可以利用它来生成语音信息:

1
2
3
4
5
6
7
8
9
10
11
$Path = "$home\Desktop\clickme.wav"

Add-Type -AssemblyName System.Speech
$synthesizer = New-Object System.Speech.Synthesis.SpeechSynthesizer
$synthesizer.SetOutputToWaveFile($Path)
$synthesizer.Rate = -10
$synthesizer.Speak('Uh, I am not feeling that well!')
$synthesizer.SetOutputToDefaultAudioDevice()

# run the WAV file (you can also double-click the file on your desktop!)
Invoke-Item -Path $Path

PowerShell 技能连载 - 合成语音(第 1 部分)

在之前的技能中,我们演示了 PowerShell 如何通过播放系统声音或 WAV 声音文件来生成声音信号。PowerShell 也可以调用内置的语音合成器:

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

请注意 Windows 10 自带了本地化的文字转语音引擎,所以如果您的 Windows 不是使用英语语言,您可能需要将以上文字转为您的语言。

可以用一系列属性来调整输出的效果。请试试这段代码:

1
2
3
4
Add-Type -AssemblyName System.speech
$synthesizer = New-Object System.Speech.Synthesis.SpeechSynthesizer
$synthesizer.Rate = -10
$synthesizer.Speak('Uh, I am not feeling that well!')

PowerShell 技能连载 - 播放声音文件

在前一个技能里我们演示了如何用 PowerShell 播放系统声音。对于更灵活一些的场景,PowerShell 也可以播放任意的 *.wav 声音文件:

1
2
3
$soundPlayer = New-Object System.Media.SoundPlayer
$soundPlayer.SoundLocation="$env:windir\Media\notify.wav"
$soundPlayer.Play()

默认情况下,PowerShell 并不会等待声音播放完毕。如果您需要同步播放声音,请试试这段代码:

1
2
3
4
$soundPlayer = New-Object System.Media.SoundPlayer
$soundPlayer.SoundLocation="$env:windir\Media\notify.wav"
$soundPlayer.PlaySync()
"Done."

要播放不同的声音文件,只需要将路径替换为声音文件即可:

sound player 也可以用后台线程循环播放一个文件:

1
2
3
$soundPlayer = New-Object System.Media.SoundPlayer
$soundPlayer.SoundLocation="$env:windir\Media\notify.wav"
$soundPlayer.PlayLooping()

当后台正在播放声音时,请确保用这段代码停止播放声音:

1
$soundPlayer.Stop()