回到基础: 理解 PowerShell 对象

PowerShell是一種強大的語言。但是是什麼使得這個腳本語言如此強大呢?PowerShell對象。這些神奇的對象是什麼,以及PowerShell如何與它們一起工作?敬請期待。

PowerShell是一種面向對象的語言和shell。這與傳統的cmd和Bash等shell不同。這些傳統的shell專注於文本,也就是字符串,雖然仍然有用,但在功能上有限。在PowerShell中,幾乎所有的東西都是對象。

在本文中,您將學習有關PowerShell中對象的一些關鍵概念。通過本文末尾的有用示例代碼和可視化,您將學會如何將這些知識應用於自己的腳本編寫中。

如果您更喜歡視覺化學習,可以觀看本文下方的相關視頻。

那麼準備好了嗎!您將經歷一次難忘的體驗,幫助您掌握PowerShell中對象的概念!

前提條件

在本文中,您將通過實際操作來學習PowerShell中的對象。如果您選擇跟隨並嘗試代碼示例,Windows PowerShell 5.1或任何版本的PowerShell 6+都應該可以使用。不過,您看到的所有示例都是在Windows 10 Build 1903上使用Windows PowerShell 5.1執行的。

理解對象的結構

在PowerShell中,對象無處不在。你可能會問自己:“對象是什麼樣子?”在這個第一部分中,你將獲得對象的概述。一旦你對對象的組成有了廣泛的了解,你就可以深入一些代碼示例了!

使用Get-Member發現對象成員

對象有許多不同類型的信息與之關聯。在PowerShell中,這些信息有時被稱為成員。對象成員是一個通用術語,指的是與對象相關的所有信息。

要發現對象的信息(成員),你可以使用Get-Member cmdlet。Get-Member cmdlet是一個方便的命令,允許你查找PowerShell中任何對象的可用屬性、方法等等。

例如,假設你想查看通過Get-Service cmdlet返回的特定對象的成員。你可以通過將Get-Service命令的輸出傳輸到Get-Member cmdlet中來實現,如下所示。

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

熟悉使用Get-Member cmdlet。在本文中,你將會經常使用它。

PowerShell中的每個輸出命令都可以將其傳輸到Get-Member。只需記住將該命令作為管道中的最後一個命令,因為它將用自己的輸出覆蓋輸出。

對象類型和類

不详细介绍面向对象编程,每个对象都有一个“模式”。对象的“模式”可以看作是一个包含创建对象蓝图的模板。这个蓝图被称为类型

PowerShell中的每个对象都有一个特定的类型。每个对象都有一个创建它的蓝图。对象类型由一个定义。考虑以下例子;9是数字Bluegill,Labrador是,等等。类位于类型之前。

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

不用深入学习这个主题。除非你是软件开发人员,否则你可能不需要太过担心语义问题。然而,基本了解这个概念是很重要的。

属性

你应该了解的关于对象的最重要的概念是属性。属性是描述对象的特征。一个对象可以有许多不同的属性,代表各种特征。

发现对象上存在的属性的最简单的方法之一是使用Get-Member命令。你可以看到,通过使用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屬性的一個子集。屬性集的主要目的是提供一個單一的屬性名稱來連接一組屬性。

還要提到對象事件的概念。事件超出了本文的範圍。

在PowerShell中使用對象

現在您對對象的組成有了基本的了解,讓我們開始動手寫一些代碼。

許多PowerShell命令產生輸出,但有時您不需要看到所有這些輸出。您需要限制或操作該輸出的某些方面。幸運的是,PowerShell有一些不同的命令,可以讓您做到這一點。

讓我們首先以列舉本地計算機上的所有服務為例,使用如下所示的Get-Service命令。您可以通過輸出看到返回了許多不同的服務(對象)。

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

控制返回的對象屬性

繼續使用Get-Service的示例,也許您不需要看到每個屬性。相反,您只需要看到StatusDisplayName屬性。要限制返回的屬性,您可以使用Select-Object命令。

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且只返回具有以A開頭的DisplayName屬性值的物件。

你可以在下面的程式碼片段中看到,在管道順序中,在Select-ObjectSort-Object之間插入了Where-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 命令创建了 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'}

但現在不再返回整個物件或僅返回幾個屬性,而是希望對每個物件返回字符串 <ServiceName> 正在執行,使用代碼 **Write-Host -ForegroundColor 'Yellow' <ServiceName> "is running"

您現在正在操作輸出並創建自己的字符串。唯一的方法是使用 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的命令,但你仍然可以通過在對象上調用Remove()方法來實現。下面的示例將介紹如何使用方法。你將在下一節中學習有關方法的知識。

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

方法簡介

在本文中,你一直在使用屬性。你讀取屬性值,創建自己的屬性,添加和刪除它們。但你對環境沒有做太多的改變。你沒有改變伺服器上的任何東西。現在讓我們使用方法來採取一些行動。

方法執行某種操作。對象存儲信息,而方法則執行操作。

例如,你可能知道Stop-Service命令。這個命令停止一個Windows服務。要做到這一點,你可以將從Get-Service獲取的對象直接發送給Stop-Service

您可以在下面看到停止 BITS 服務的示例。此示例停止 BITS 服務,然後檢查狀態以確保已停止。您執行了兩個動作:停止服務和檢查狀態。

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/