回归基础:理解 PowerShell 对象

PowerShell 是一种强大的语言。但是是什么让这种脚本语言如此强大呢?PowerShell 对象。这些神奇的对象是什么,PowerShell 如何使用它们?敬请关注。

PowerShell 是一种面向对象的语言和 shell。这与传统的命令行 shell(如 cmd 和 Bash)不同。这些传统的 shell 侧重于文本,也就是字符串,虽然仍然有用,但在功能上有限。在 PowerShell 中,几乎所有东西都是对象。

在本文中,您将了解有关 PowerShell 中对象的一些关键概念。通过本文的最后,您将学会如何将这些知识应用到您自己的脚本中,通过有用的示例代码和可视化展示。

如果您更喜欢通过视觉学习,请随时观看本文下方的配套视频。

所以,做好准备吧!您将体验到一次令人难忘的经历,这将帮助您掌握 PowerShell 中对象的概念!

先决条件

在本文中,您将通过实践方法了解 PowerShell 中的对象。如果您选择跟着尝试代码示例,Windows PowerShell 5.1 或任何 PowerShell 6+ 版本都应该可以。但是,请注意,您看到的所有示例都是在 Windows 10 版本 1903 上使用 Windows PowerShell 5.1 执行的。

理解对象的解剖结构

对象在 PowerShell 中随处可见。你可能会问自己:“一个对象是什么样子的?”在这个第一部分,你将会对对象的组成有一个概览。一旦你对对象是什么有了一个广泛的认识,你就可以深入一些代码示例了!

使用 Get-Member 发现对象成员

对象具有许多不同类型的信息与其关联。在 PowerShell 中,这些信息有时被称为成员。对象成员是一个通用术语,指的是与对象关联的所有信息。

要发现对象的信息(成员),你可以使用Get-Member 命令。Get-Member 命令是一个方便的命令,允许你查找 PowerShell 中任何对象的可用属性、方法等等。

例如,假设你想要查看通过Get-Service 命令返回的特定对象的成员。你可以通过将Get-Service 命令的输出传递给Get-Member 命令来执行此操作,如下所示。

language-powershell
Get-Service -ServiceName 'BITS' | Get-Member

要习惯使用Get-Member 命令。在本文中,你将经常使用它。

PowerShell 中的每个生成输出的命令都可以传递给Get-Member。只需记住将此命令置于管道中的最后位置,因为它将用自己的输出覆盖输出。

对象类型和类

不深入讨论面向对象编程,每个对象都有一个“模式”。对象的“模式”可以看作是一个模板,其中包含创建对象的蓝图。该蓝图称为类型

PowerShell中的每个对象都有一个特定的类型。每个对象都有一个用于创建它的蓝图。对象类型由一个定义。考虑这个例子;9是数字蓝鳃鱼,拉布拉多狗,等等。类在类型之前。

对象是具有特定类型的类的实例

不要担心深入研究这个话题。除非你是软件开发人员,否则你现在可能不需要太担心语义。然而,这是一个基本水平上了解的重要概念。

属性

关于对象,你应该了解的最重要的概念是属性。属性是描述对象的属性。一个对象可以有许多不同的属性附加到它上面,表示各种属性。

发现对象上存在哪些属性的最简单方法之一是使用Get-Member cmdlet。你可以看到,通过使用MemberType参数,Get-Member将限制返回的输出仅为对象。你还会看到它显示了System.ServiceProcess.ServiceController的对象类型。

PS51> Get-Service | Get-Member -MemberType Property
Get-Service object properties

现在以前面介绍的BITS服务为例,使用下面的代码查看该对象属性的具体值。Get-Member cmdlet允许你查找属性名称,但不提供属性的值。然而,使用PowerShell的Select-Object cmdlet,你可以检查属性的值。

下面你可以看到StartType是该对象的一个属性。返回的对象有许多不同的成员,但通过使用Select-Object cmdlet,它限制输出仅显示该属性。

PS51> Get-Service -ServiceName 'BITS' | Select-Object -Property 'StartType'
BITS service start type

属性是在PowerShell中处理对象时最常见的组件。

别名

一些属性的MemberTypeAlias。别名是属性名称的伪名,有时可以为属性提供更直观的名称。

你可以看到使用Get-Service cmdlet的对象示例,如下所示。属性Name的别名是ServiceNameRequiredServices的别名是ServicesDependedOn属性。

PS51> Get-Service | Get-Member -MemberType 'AliasProperty'
AliasProperty members on service objects

当属性有别名时,你可以使用别名而不是实际属性名称引用该属性的值。在这个例子中,像NameRequiredServices这样的描述性属性比ServiceNameServicesDependedOn更直观,更容易输入。

你可以在下面看到引用这些别名的示例。

#使用AliasProperty代替实际属性名称
PS51> $Svc = Get-Service -ServiceName 'BITS' #您正在使用的对象
PS51> $Svc.Name
BITS
PS51> $Svc.RequiredServices

您应该看到以下输出。请注意,无论使用别名与否,信息都是相同的:

Properties on the BITS service object

方法

属性只是创建对象的一部分;方法也是一个重要的概念。方法是可以在对象上执行的操作。与属性一样,您可以使用Get-Member cmdlet来发现对象上的方法。

要将Get-Member的输出限制为仅包含方法,将MemberType参数值设置为Method,如下所示。

PS51> Get-Service | Get-Member -MemberType 'Method'
Methods on service objects

作为初学者,您会比属性更少地使用方法。

其他的MemberTypes

属性、方法和别名并不是对象可能具有的唯一类型成员。然而,它们将是您工作中最常见的类型成员。

为了完整起见,以下是您可能会遇到的其他几种类型的成员。

  • 脚本属性 – 用于计算属性值。
  • 注意属性 – 用于静态属性名称。
  • 属性集 – 这些就像包含正如名称所暗示的内容的别名一样;一组属性。例如,您为您的Get-CompInfo函数创建了一个自定义属性称为SpecsSpecs实际上是属性Cpu、Mem、Hdd、IP的子集。属性集的主要目的是提供一个单一的属性名称来连接一组属性。

It’s also important to mention the concept of object events. 事件超出了本文的范围。

在PowerShell中使用对象

现在您已经基本了解了对象的组成,让我们开始动手编写一些代码吧。

许多PowerShell命令会产生输出,但有时您不需要看到所有这些输出。您需要以某种方式限制或操作该输出。幸运的是,PowerShell有一些不同的命令,可以让您做到这一点。

让我们从使用Get-Service cmdlet列举本地计算机上的所有服务的示例开始。您可以通过输出看到返回了许多不同的服务(对象)。

PS51> Get-Service -ServiceName *
Using a wildcard on ServiceName parameter

控制返回的对象属性

继续使用Get-Service示例,也许您不需要看到每个属性。相反,您只需要看到StatusDisplayName属性。要限制返回的属性,您将使用Select-Object cmdlet。

Select-Object cmdlet通过“过滤”各种属性,使其不会返回到PowerShell管道中。要过滤返回的对象属性,可以使用Property参数,并指定要返回的一个或多个属性的逗号分隔集。

您可以看到下面的示例,仅返回StatusDisplayName属性。

PS51> Get-Service -ServiceName * | Select-Object -Property 'Status','DisplayName'
Showing the Status and DisplayName properties

对象排序

也许您正在构建一个报告,显示服务及其状态。为了方便信息消化,您想要按Status属性的值对返回的对象进行排序。为此,可以使用Sort-Object cmdlet。

Sort-Object cmdlet允许您收集所有返回的对象,然后按您定义的顺序输出它们。

例如,使用Sort-ObjectProperty参数,可以指定从Get-Service接收的对象中要排序的一个或多个属性。PowerShell将每个对象传递给Sort-Object cmdlet,然后按属性值对它们进行排序。

您可以看到下面的示例,返回按其Status属性排序的所有服务对象,使用Sort-ObjectDescending开关参数以降序返回。

PS51> Get-Service -ServiceName * | Select-Object -Property 'Status','DisplayName' |
	Sort-Object -Property 'Status' -Descending
Using Sort-Object to sort service objects by Status in descending order

在PowerShell中,管道[|]是您在必要时应使用的几种行连续技术之一。与使用反引号相比,使用它更好

过滤对象

也许你决定不想看到机器上的所有服务。相反,你需要按特定标准限制输出。通过使用Where-Object命令,可以过滤返回的对象数量。

虽然Select-Object命令限制了特定属性的输出,但Where-Object命令限制了整个对象的输出。

Where-Object命令在功能上类似于SQL WHERE 子句。它作为原始源的过滤器,只返回符合特定标准的某些对象。

也许你决定只希望返回具有Status属性值为Running的对象,并且只有那些DisplayName属性值以A开头的对象。

你可以在下一个代码片段中看到Where-Object引用被插入到了管道顺序中的Select-ObjectSort-Object之间。通过使用一个脚本块值,其中包含了每个对象需要满足的必需条件,通过FilterScript参数,你可以制作任何类型的查询。

如果你想要操纵输出返回到控制台的方式,请查看Format-Table命令。

PS51> Get-Service * | Select-Object -Property 'Status','DisplayName' |
	Where-Object -FilterScript {$_.Status -eq 'Running' -and $_.DisplayName -like "Windows*" |
		Sort-Object -Property 'DisplayName' -Descending | Format-Table -AutoSize
Formatting object output

计数和平均返回的对象

Get-Service命令返回许多不同的对象。使用Where-Object cmdlet,您已经过滤掉了其中的一部分对象,但是有多少个呢?介绍一下Measure-Object cmdlet。

Measure-Object cmdlet是PowerShell命令之一,可以对通过管道接收到的对象进行数学运算之一是计算它接收到了多少个对象。

也许您想知道最终在合并命令运行时返回了多少对象。您可以将最终输出导向Measure-Object cmdlet以找到返回的对象的总数,如下所示。

PS51> Get-Service * | Select-Object -Property 'Status','DisplayName' |
	Where-Object {$_.Status -eq 'Running' -and $_.DisplayName -like "Windows*" |
		Sort-Object -Property 'DisplayName' -Descending | Measure-Object

一旦命令处理完成,您将会看到,在这种情况下,最初由Get-Service cmdlet创建的对象有21个被返回。

Finding the number of objects returned with Measure-Object

也许您只想知道返回的对象总数。由于Measure-Object命令返回通过Count属性找到的总对象数,因此您可以再次引用Select-Object cmdlet。但是这次,只返回Count属性。

PS51> Get-Service * | Select-Object -Property 'Status','DisplayName' |
	Where-Object {$_.Status -eq 'Running' -and $_.DisplayName -like "Windows*" |
		Sort-Object -Property 'DisplayName' -Descending |
			Measure-Object |
				#我们重新开始,首先过滤,最后格式化
				Select-Object -Property 'Count'
Only returning the count property

对对象进行循环处理

通过管道处理每个对象时,您可以使用循环对每个对象采取行动。PowerShell中有不同类型的循环,但是在管道示例中,让我们看看ForEach-Object cmdlet。

ForEach-Object cmdlet允许您对流入它的每个对象采取行动。这种行为最好通过示例来解释。

继续使用 Get-Service 示例,也许您想查找在 Windows 计算机上名称以 “Windows” 开头并正在运行的所有服务。 使用 Where-Object cmdlet,您可以像之前一样创建条件。

Where-Object -FilterScript {$_.DisplayName -Like "Windows*" -and $_.Status -eq 'Running'}

但现在,您不想返回整个对象或仅返回几个属性,而是希望对每个对象使用代码 **Write-Host -ForegroundColor 'Yellow' <ServiceName> "is running" 返回字符串 <ServiceName> 正在运行

您现在正在处理输出并创建自己的字符串。 唯一的方法是使用如下所示的 ForEach-Object cmdlet。对于 通过 Where-Object 返回的每个对象,PowerShell 运行代码 Write-Host -ForegroundColor 'Yellow' $_.DisplayName "is running"

PS51> Get-Service -ServiceName * |
	Where-Object {$_.DisplayName -Like "Windows*" -and $_.Status -eq 'Running'} | 
		Foreach-Object {
			Write-Host -ForegroundColor 'Yellow' $_.DisplayName "is running"
		}
Filtering by property with Where-Object

ForEach-Object cmdlet 在许多方面都很有用。 例如,您可以构建额外的逻辑,枚举找到的每个服务对象,并根据属性值更改输出字符串的颜色和措辞,甚至执行其他操作,如启动已停止的服务。

想象一下这为您提供的可能性! 经过一点思考和规划,您可以创建一个脚本,只需一个命令即可轻松地在许多对象上执行它。

比较对象

有时您需要查看两个对象并比较属性值。

也许你的网络上有两个几乎相同的系统。但是,你遇到了一个你认为是配置问题的服务,出现在其中一个系统上。

你得出的结论是,由于这些系统位于网络的不同部分,你需要使用远程命令来在 PowerShell 会话中收集信息。你打开你喜欢的编辑器并创建一个脚本。正如你下面所看到的,这个脚本连接到两个不同的服务器,并枚举了每个服务器上的所有进程。

$A = 'Svr01a.contoso.com'
$B = 'Svr02b.contoso.com'

$ProcA = Invoke-Command -Computername $A -Scriptblock {Get-Process -Name *}
$ProcB = Invoke-Command -ComputerName $B -Scriptblock {Get-Process -Name *}

现在,你已经捕获了每台计算机上的所有进程,分别存在 $ProcA$ProcB 变量中。现在你需要比较它们。你可以手动查看每组进程,或者你可以选择简单的方式,使用一个叫做 Compare-Object 的 cmdlet。

Compare-Object 允许你比较两个不同对象的属性值。这个 cmdlet 读取每个对象中的属性,查看它们的值,然后返回默认情况下不同的内容,以及相同的内容。

要使用 Compare-Object,请指定一个 ReferenceObject 和一个 DifferenceObject 参数,并将每个对象作为参数值提供,如下所示。

Compare-Object -ReferenceObject $ProcA -DifferenceObject $ProcB

默认情况下,Compare-Object 只会返回由 SideIndicator 属性指示的对象的差异。用于表示比较对象匹配的符号或 边指示器><=

Running Compare-Object

你可以使用 Compare-Object 的开关参数 IncludeEqual,返回属性相同的对象。如果是这样,你会看到 == 作为侧边指示符。类似地,你可以使用 ExcludeDifferent 来排除不同之处。

Compare-Object 命令是一个非常有用的命令。如果你想了解更多,请务必访问 在线文档

使用自定义对象

现在你已经很好地理解了对象是什么,以及如何使用它们,现在是时候创建你自己的对象了!

使用哈希表创建自定义对象

创建自定义对象的一种方法是使用哈希表。哈希表是一组键/值对,正是你创建对象属性所需的。

让我们从使用哈希表创建一个带有一些键/值的自定义 PowerShell 对象开始。在下面的示例中,你正在创建一个哈希表。这个哈希表表示一个单一对象及其属性。一旦定义了哈希表 $CarHashtable,你就可以使用 PsCustomObject 类型加速器 来转换。

pscustomobject类型加速器是创建pscustomobject类实例的快速方式。这种行为称为类型转换

在下面的代码片段结束时,您将拥有一个类型为pscustomobject的对象($CarObject),并为其分配了五个属性。

## 定义哈希表
$CarHashtable = @{
	Brand      = 'Ford'
	Style      = 'Truck'
	Model      = 'F-150'
	Color      = 'Red'
	Drivetrain = '4x4'
}

## 创建对象
$CarObject = [PsCustomObject]$CarHashTable

或者,您也可以使用New-Object cmdlet。使用相同的哈希表,而不是使用pscustomobject类型加速器,您可以使用New-Object的长形式。下面显示了一个示例。

$CarHashtable = @{
	Brand      = 'Ford'
	Style      = 'Truck'
	Model      = 'F-150'
	Color      = 'Red'
	Drivetrain = '4x4'
}
#创建对象
$CarObject = New-Object -TypeName PsObject -Properties $CarHashtable

$CarObject被创建时,您现在可以看到您可以引用每个属性,就像它来自于一个内置的PowerShell cmdlet,比如Get-Service

Inspecting object properties

添加和删除属性

不仅可以创建自定义对象,还可以向其中添加内容。回想一下使用Get-Member cmdlet吗?Get-Member有一个相关的叫做Add-MemberAdd-Member cmdlet不会枚举成员,而是添加它们。

以先前创建的自定义对象为例,也许您想要向该对象添加一个车型年份属性。您可以通过将对象传递到Add-Member来实现这一点,指定:

  • 成员的类型(在这种情况下是简单的NoteProperty
  • 属性的名称(Year
  • 属性的值(Value

您可以在下面看到一个示例。

PS51> $CarObject | Add-Member -MemberType NoteProperty -Name 'Year' -Value '2010'

再次看到下面,它看起来就像任何其他属性一样。

Adding and viewing a new property

您可以使用类似的技术添加许多不同类型的成员。如果您想自行探索更多,请查看Add-Member 文档

您可以轻松地从对象中删除成员。虽然没有Remove-Member cmdlet,但您仍然可以通过在对象上调用Remove()方法来实现,如下所示。您将在下一节了解有关方法的信息。

PS51> $CarObject.psobject.properties.remove('Drivetrain')

快速介绍方法

在本文中,您一直在处理属性。您已读取属性值,创建了自己的属性并添加和删除了它们。但是您并没有真正改变环境。您没有在服务器上做任何更改。让我们使用方法采取一些行动。

方法执行某种操作。对象存储信息,而方法执行操作。

例如,您可能知道Stop-Service命令。此命令停止Windows服务。为此,您可以将从Get-Service直接发送到Stop-Service的对象。

您可以在下面看到停止 BITS 服务的示例。此示例停止 BITS 服务,然后检查状态以确保已停止。您已经使用 cmdlet 执行了两个操作;停止服务和检查状态。

PS51> Get-Service -ServiceName 'BITS' | Stop-Service
PS51> Get-Service -ServiceName 'BITS'

与其两次运行 Get-Service 并执行单独的命令 Stop-Service,您可以利用内置到服务对象中的方法。许多对象都有方法。在这种情况下,Stop-Service 甚至不需要第二个 Get-Service 引用。

Running commands to stop and start a service

通过在服务对象本身上调用方法,您可以停止并检索更新后的状态,所有这些都是使用单个对象完成的。下面您可以看到这一点。注意,通过使用  Stop()Start() 方法,您可以像命令一样操作服务。

为了确保服务状态更改后的 Status 属性值是最新的,您可以调用 Refresh() 方法,这类似于另一个 Get-Service 命令调用。

## 停止本地计算机上的 BITS 服务
$Svc = Get-Service -ServiceName 'BITS' # 正在操作的对象
$Svc.Stop() # 执行的方法/操作
$Svc.Refresh() # 执行的方法/操作
$Svc.Status # 属性

# 启动本地计算机上的 BITS 服务
$Svc = Get-Service -ServiceName 'BITS' # 正在操作的对象
$Svc.Start() # 执行的方法/操作
$Svc.Refresh() # 执行的方法/操作
$Svc.Status # 属性

您应该看到以下输出:

Executing methods on the service object

有关方法的更多信息,请查看about_Methods 帮助主题

结论

PowerShell 中有许多可以使用对象完成的任务。本文只是一个入门指南,帮助您开始学习它们。在本文中,您已经了解了关于对象的一些基础知识,以及如何执行操作、操作和创建它们。通过代码示例,您已经看到了一些不同的场景,展示了如何执行这些任务。保持冷静,学习 PowerShell。感谢阅读!

Source:
https://adamtheautomator.com/powershell-objects/