PowerShell 技能连载 - 带颜色的控制台硬拷贝

如果您想硬拷贝 PowerShell 控制台的内容,则可以复制和选择文本,但这会弄乱颜色和格式。

更好的方法是读取控制台屏幕缓冲区,并编写 HTML 文档。然后可以将这些 HTML 文档复制并粘贴到 Word 和其他目标中,并保持格式和颜色。

以下代码当然还不是完美的,但说明了采用的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function Get-ConsoleBufferAsHtml{
$html = [Text.StringBuilder]'' $null = $html.Append("<pre style='MARGIN: 0in 10pt 0in; line-height:normal'; font-family:Consolas; font-size:10pt; >")
$bufferWidth = $host.UI.RawUI.BufferSize.Width $bufferHeight = $host.UI.RawUI.CursorPosition.Y $rec = [Management.Automation.Host.Rectangle]::new(
0,0,($bufferWidth - 1),$bufferHeight )
$buffer = $host.ui.rawui.GetBufferContents($rec)

for($i = 0; $i -lt $bufferHeight; $i++)
{
$span = [Text.StringBuilder]'' $foreColor = $buffer[$i, 0].Foregroundcolor $backColor = $buffer[$i, 0].Backgroundcolor for($j = 0; $j -lt $bufferWidth; $j++)
{
$cell = $buffer[$i,$j]
if (($cell.ForegroundColor -ne $foreColor) -or ($cell.BackgroundColor -ne $backColor))
{
$null = $html.Append("<span style='color:$foreColor;background:$backColor'>$($span)</span>" )
$span = [Text.StringBuilder]'' $foreColor = $cell.Foregroundcolor $backColor = $cell.Backgroundcolor }
$null = $span.Append([Web.HttpUtility]::HtmlEncode($cell.Character))

}
$null = $html.Append("<span style='color:$foreColor;background:$backColor'>$($span)</span><br/>" )
}

$null = $html.Append("</pre>")
$html.ToString()
}

请注意,此功能需要一个真实的控制台窗口,因此在 PowerShell ISE 中将无法使用。当您运行上述代码时,它将为您提供一个名为 Get-ConsoleBufferAsHtml 的新命令。

要将当前控制台内容硬拷贝到 HTML 文件,请运行以下命令:

1
PS>  Get-ConsoleBufferAsHtml | Set-Content $env:temp\test.html

要在关联的浏览器中打开生成的 HTML,请运行以下命令:

1
PS>  Invoke-Item $env:temp\test.html

PowerShell 技能连载 - 理解 $ErrorView

当 PowerShell 遇到问题时,它会显示一条相当长的错误消息:

1
2
3
4
5
6
7
PS> 1/0
Attempted to divide by zero.
At line:1 char:1
+ 1/0
+ ~~~
+ CategoryInfo : NotSpecified: (:) [], RuntimeException
+ FullyQualifiedErrorId : RuntimeException

在现实生活中,您通常只需要第一行,而早在 2006 年,PowerShell 团队就添加了一个名为 $ErrorView 的首选项变量,该变量可以控制错误消息的显示方式。当将它赋值为“CategoryView”时,将大大缩短错误消息:

1
2
3
4
5
6
PS> $ErrorView = 'CategoryView'

PS> 1/0
NotSpecified: (:) [], RuntimeException

PS>

不过,这并不太好用,因为一行无法体现真正重要的信息,并且您可以将它赋值为“NormalView”以返回到默认视图。同时,实用性较差导致多数人从未听说过 $ErrorView

幸运的是,在PowerShell 7(RC1 中引入)中,团队记住并最终解决了该问题。为了不破坏兼容性,他们选择添加第三个选项:ConciseView。现在,单行错误消息可以正常工作,并显示典型用户需要知道的所有信息:

1
2
3
4
5
6
PS> $ErrorView = ConciseView

PS> 1/0
RuntimeException: Attempted to divide by zero.

PS>

作为 PowerShell 开发人员,只需切换回“NormalView”即可查看其余的错误消息。或者(甚至更好)运行 Get-Error -Newest 1 以获取有关最新错误的详细信息:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
PS C:\> 1/0
RuntimeException: Attempted to divide by zero.
PS C:\> Get-Error -Newest 1

Exception            :
  Type         : System.Management.Automation.RuntimeException
  ErrorRecord  :
      Exception            :
          Type  : System.Management.Automation.ParentContainsErrorRecordException
          Message : Attempted to divide by zero.
          HResult : -2146233087
      CategoryInfo         : NotSpecified: (:) [], ParentContainsErrorRecordException
      FullyQualifiedErrorId : RuntimeException
      InvocationInfo      :
          ScriptLineNumber : 1
          OffsetInLine    : 1
          HistoryId      : -1
          Line            : 1/0
          PositionMessage : At line:1 char:1
                             + 1/0
                             + ~~~
          CommandOrigin  : Internal
      ScriptStackTrace     : at , : line 1
  TargetSite    :
      Name         : Divide
      DeclaringType : System.Management.Automation.IntOps, System.Management.Automation, Version=7.0.0.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35
      MemberType  : Method
      Module      : System.Management.Automation.dll
  StackTrace    :
 at System.Management.Automation.IntOps.Divide(Int32 lhs, Int32 rhs)
 at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
 at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
 at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
  Message      : Attempted to divide by zero.
  Data         : System.Collections.ListDictionaryInternal
  InnerException :
      Type    : System.DivideByZeroException
      Message : Attempted to divide by zero.
      HResult : -2147352558 ;
  Source       : System.Management.Automation
  HResult      : -2146233087
CategoryInfo          : NotSpecified: (:) [], RuntimeException
FullyQualifiedErrorId : RuntimeException
InvocationInfo    :
  ScriptLineNumber: 1
  OffsetInLine    : 1
  HistoryId       : -1
  Line            : 1/0
  PositionMessage : At line:1 char:1
                     + 1/0
                     + ~~~
  CommandOrigin  : Internal
ScriptStackTrace     : at , : line 1

PowerShell 技能连载 - 参数的智能感知(第 4 部分)

如果输入参数时会为用户建议有效的参数,那岂不是很棒?有时候它们会提示。当您键入以下命令并在 -LogName 之后按空格时,PowerShell ISE 和 Visual Studio Code 会弹出一个 IntelliSense 菜单,其中包含您可以转储的所有日志文件:

1
PS> Get-EventLog -LogName

如果没有弹出自动 IntelliSense(换句话说在 PowerShell 控制台中),则可以按 TAB 键自动完成操作,或者按 CTRL + SPACE 手动强制显示 IntelliSense 选择项。

您可以使用自己的 PowerShell 函数执行相同的操作,并且有多种方法可以执行此操作。在上一个技能中,我们研究了 “ValidateSet“ 属性。今天,我们来看看一个更超级隐蔽的类似属性,名为 “ArgumentCompleter“.

通过 ValidateSet,您可以定义一系列用户可以选择的值。其它值都不允许输入。

如果您想向用户提供建议(例如)最常用的服务器,但又允许用户指定完全不同的服务器怎么办?这正是 “ArgumentCompleter“ 属性起作用的时候。它定义了一个建议值的列表,但并不限制用户使用这些值:

1
2
3
4
5
6
7
8
9
10
function Get-Vendor {
param(
[Parameter(Mandatory)]
[ArgumentCompleter({'Microsoft','Amazon','Google'})]
[string]
$Vendor
)

"Chosen vendor: $Vendor"
}

在交互式 PowerShell 控制台中运行此命令并调用 Get-Vendor 时,现在可以按 TAB 或 CTRL + SPACE 自动完成或打开 IntelliSense 列表。不过,该属性不会自动为您弹出 IntelliSense 菜单,并且在 PowerShell 编辑器的编辑器窗格中可能无法使用。

尽管如此,”ArgumentCompleter“ 属性还是有很大帮助的,特别是对于经常使用命令和制表符完成功能的高级用户而言。通过为参数添加默认选项,用户可以快速浏览这些选项,但也可以提交任何其他参数。

PowerShell 技能连载 - 参数的智能感知(第 3 部分)

如果输入参数时会为用户建议有效的参数,那岂不是很棒?有时候它们会提示。当您键入以下命令并在 -LogName 之后按空格时,PowerShell ISE 和 Visual Studio Code 会弹出一个 IntelliSense 菜单,其中包含您可以转储的所有日志文件:

1
PS> Get-EventLog -LogName

如果没有弹出自动 IntelliSense(换句话说在 PowerShell 控制台中),则可以按 TAB 键自动完成操作,或者按 CTRL + SPACE 手动强制显示 IntelliSense 选择项。

您可以使用自己的 PowerShell 函数执行相同的操作,并且有多种方法可以执行此操作。在上一个技能中,我们研究了使用自定义枚举类型。今天,我们来看看更简单的方法(以及一些实现它的隐藏技巧).

使用 IntelliSense 提供参数的最简单方法可能是使用属性 “ValidateSet”:您只需使用允许的值的列表来限定参数:

1
2
3
4
5
6
7
8
9
10
11
function Get-Vendor
{
param(
[Parameter(Mandatory)]
[ValidateSet('Microsoft','Amazon','Google')]
[string]
$Vendor
)

"Chosen vendor: $Vendor"
}

$vendor 变量是 “string“ 类型的,但是 PowerShell 内部会确保只能赋值为 “ValidateSet” 中列出的值。您也可以对常规变量使用相同的技巧,并为代码增加额外的安全性:

1
2
3
4
5
6
7
[ValidateSet('dc1','dc2','ms01')]$servers = 'dc1'

# works
$servers = 'dc2'

# fails
$servers = 'dc3'

以下是另一个技巧:”ValidateSet” 属性仅适用于变量和参数分配,而不适用于参数设置值。作为函数作者,您可以将一个用户不可选的值作为缺省值赋给参数:

1
2
3
4
5
6
7
8
9
function Get-Vendor {
param(
[ValidateSet('Microsoft','Amazon','Google')]
[string]
$Vendor = 'Undefined'
)

"Chosen vendor: $Vendor"
}

当用户调用不带参数的 Get-Vendor 时,$vendor 将设置为 “Undefined”。用户为参数分配值后,该值将不可用,从而轻松帮助您区分用户是否进行了选择。

PowerShell 技能连载 - 参数的智能感知(第 2 部分)

如果输入参数时会为用户建议有效的参数,那岂不是很棒?有时候它们会提示。当您键入以下命令并在 -LogName 之后按空格时,PowerShell ISE 和 Visual Studio Code 会弹出一个 IntelliSense 菜单,其中包含您可以转储的所有日志文件:

1
PS> Get-EventLog -LogName

如果没有弹出自动 IntelliSense(换句话说在 PowerShell 控制台中),则可以按 TAB 键自动完成操作,或者按 CTRL + SPACE 手动强制显示 IntelliSense 选择项。

您可以使用自己的 PowerShell 函数执行相同的操作,并且有多种方法可以执行此操作。在上一个技能中,我们研究了使用枚举类型。但是,如果没有您想要向用户建议的枚举类型定义值,该怎么办?

要么使用“Enum""关键字定义自己的枚举类型(在 PowerShell 5 或更高版本中支持):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Enum MyVendors
{
Microsoft
Amazon
Google
}

function Get-Vendor
{
param(
[Parameter(Mandatory)]
[MyVendors]
$Vendor
)

"Chosen vendor: $Vendor"
}

或使用 Add-Type(所有 PowerShell 版本都支持)使用 C# 创建自己的枚举(请参见下文)。请注意,C# 代码区分大小写,并且枚举内的值以逗号分隔。“enum” 关键字后面的词定义了枚举的类型名称。使用该名称作为参数的数据类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$definition = 'public enum VendorList
{
Microsoft,
Amazon,
Google
}'
Add-Type -TypeDefinition $definition


function Get-Vendor
{
param(
[Parameter(Mandatory)]
[VendorList]
$Vendor
)

"Chosen vendor: $Vendor"
}

还要注意,Add-Type 不能编辑或覆盖类型,因此,如果要在使用枚举后更改枚举,则需要重新启动 PowerShell 或重命名枚举。而使用更新的 PowerShell “enum” 关键字,您可以随时更改枚举。

通过这两种方式,当用户调用您的函数并使用参数时,IntelliSense 都会列出可用的值。

1
2
PS> Get-Vendor -Vendor Amazon
Chosen vendor: Amazon

注意:将函数导出到模块时,请确保还将枚举也添加到模块中。必须先定义枚举类型,才能在调用使用这些枚举的函数。

PowerShell 技能连载 - 参数的智能感知(第 1 部分)

如果输入参数时会为用户建议有效的参数,那岂不是很棒?有时候它们会提示。当您键入以下命令并在 -LogName 之后按空格时,PowerShell ISE 和 Visual Studio Code 会弹出一个 IntelliSense 菜单,其中包含您可以转储的所有日志文件:

1
PS> Get-EventLog -LogName

如果没有弹出自动 IntelliSense(换句话说在 PowerShell 控制台中),则可以按 TAB 键自动完成操作,或者按 CTRL + SPACE 手动强制显示 IntelliSense 选择项。

您可以使用自己的 PowerShell 函数执行相同的操作,并且有多种方法可以执行此操作。今天让我们来看一下使用枚举类型的方法。

将枚举类型分配给参数时,它将自动列出可用值。下面的代码使用了 [System.ConsoleColor] 类型,该类型列出了所有有效的控制台颜色:

1
2
3
4
5
6
7
8
9
10
function Set-ErrorColor
{
param(
[Parameter(Mandatory)]
[System.ConsoleColor]
$Color
)

$Host.PrivateData.ErrorForegroundColor = [string]$Color
}

当您调用 Set-ErrorColor 时,PowerShell 会自动向您建议有效的控制台颜色。当您选择一个时,该函数将此颜色分配给错误前景色。如果您不喜欢粗糙的红色错误颜色,请将错误消息变成绿色以使其更友好:

1
2
3
4
5
6
7
8
9
PS> Set-ErrorColor -Color Green

PS> 1/0
Attempted to divide by zero.
At line:1 char:1
+ 1/0
+ ~~~
+ CategoryInfo : NotSpecified: (:) [], RuntimeException
+ FullyQualifiedErrorId : RuntimeException

旁注:由您来决定对所选类型的处理方式。有时,将其转换为其他类型可能更理想。例如,在上面的示例中,选择的颜色将转换为字符串。为什么呢?

因为只有 PowerShell 控制台窗口支持 ConsoleColor 颜色。而 PowerShell ISE 编辑器等支持更多颜色,并使用 [System.Windows.Media.Color] 类型。

由于可以将字符串转换为这两种类型,但是 ConsoleColor 不能直接转换为 Windows.Media.Color,因此您可以将其转换为更通用的类型字符串,实现同时在控制台和 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
# string converts to ConsoleColor
PS> [ConsoleColor]'red'
Red

# string converts to System.Windows.Media.Color
PS> [System.Windows.Media.Color]'red'


ColorContext :
A : 255
R : 255
G : 0
B : 0
ScA : 1
ScR : 1
ScG : 0
ScB : 0

# ConsoleColor DOES NOT convert to System.Windows.Media.Color
PS> [System.Windows.Media.Color][ConsoleColor]'red'
Cannot convert value "Red" to type "System.Windows.Media.Color". Error: "Invalid cast from 'System.ConsoleColor' to
'System.Windows.Media.Color'."
At line:1 char:1
+ [System.Windows.Media.Color][ConsoleColor]'red'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvalidCastIConvertible

PowerShell 技能连载 - 区分 IPv4 和 IPv6

假设您要获取所有网卡的IP地址,但按地址类型将它们分开。这是一种仅使用 Select-Object 的实现方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Get-IPAddress
{
Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration |
Where-Object { $_.IPEnabled -eq $true } |
# add two new properties for IPv4 and IPv6 at the end
Select-Object -Property Description, MacAddress, IPAddress, IPAddressV4, IPAddressV6 |
ForEach-Object {
# add IP addresses that match the filter to the new properties
$_.IPAddressV4 = $_.IPAddress | Where-Object { $_ -like '*.*.*.*' }
$_.IPAddressV6 = $_.IPAddress | Where-Object { $_ -notlike '*.*.*.*' }
# return the object
$_
} |
# remove the property that holds all IP addresses
Select-Object -Property Description, MacAddress, IPAddressV4, IPAddressV6
}

Get-IPAddress

结果看起来类似这样:

Description                          MacAddress        IPAddressV4   IPAddressV6
-----------                          ----------        -----------   -----------
Realtek USB GbE Family Controller #3 00:E0:4C:F4:A9:35 10.4.121.75   fe80::8109:a41e:192b:367

PowerShell 技能连载 - 安装并试运行 Windows Terminal

Windows Terminal 是一个新的多选项卡的用于基于控制台的外壳程序。它可以通过 Microsoft Store 正式获得,当前需要 Windows 10 1903 或更高版本。

要从 Microsoft Store 安装它,您首先需要注册,这很令人沮丧。如果要匿名安装 Windows Terminal(并且已在上一个技巧中安装Chocolatey),只需从提升的 PowerShell 中运行以下代码:

1
2
3
4
5
6
7
# download installation code
$code = Invoke-WebRequest -Uri 'https://chocolatey.org/install.ps1' -UseBasicParsing
# invoke installation code
Invoke-Expression $code

# install windows terminal
choco install microsoft-windows-terminal -y

一旦 Windows Terminal 安装好,您可以通过 “wt” 命令启动它。

PowerShell 技能连载 - 安装免费的 Chocolatey 包管理器

Chocolatey 是一个软件包管理系统,可以帮助您下​​载和安装软件包。与 PowerShell Gallery 不同,Chocolatey 不仅限于 PowerShell 模块和脚本,还可以安装各种软件,包括 Notepad ++、Acrobat Reader 或 Chrome 浏览器之类的工具。

如果您准备在具有提升至完整管理员权限的 Shell 中运行 Chocolatey,则很简单。尽管有说明如何在没有完全特权的情况下使 Chocolatey 工作,但几乎肯定会遇到问题。

要在 PowerShell 中使用Chocolatey,请下载其安装脚本并运行它。这需要管理员特权:

1
2
3
4
5
# download installation code
$code = Invoke-WebRequest -Uri 'https://chocolatey.org/install.ps1' -UseBasicParsing

# invoke installation code
Invoke-Expression $code

这步之后,您可以在 PowerShell 中使用新的“choco”命令。只需确保在提升权限的 Shell 中运行即可。

可以在此处找到可安装软件包的列表(例如 Acrobat Reader 或 Google Chrome):

https://chocolatey.org/packages

例如,要安装 Chrome 浏览器,请运行以下命令:

1
choco install googlechrome -y

同样,当您从未提升的 PowerShell 中运行“choco”时,您会收到一条警告消息,并且大多数软件包将无法正确安装。