您想要瞭解 PSCustomObject 的一切
PSCustomObject
是新增至PowerShell工具帶的絕佳工具。 讓我們從基本概念開始,並著手進行更進階的功能。 使用 背後的 PSCustomObject
概念是有一個簡單的方法來建立結構化數據。 看看第一個範例,您將進一步瞭解這是什麼意思。
注意
本文的原始版本出現在@KevinMarquette撰寫的部落格上。 PowerShell 小組感謝 Kevin 與我們分享此內容。 請查看他在 PowerShellExplained.com 的部落格。
建立 PSCustomObject
[PSCustomObject]
我喜歡在 PowerShell 中使用 。 建立可使用的對象從未變得更容易。
因此,我會略過所有其他方式,您可以建立物件,但我需要提及這些範例中大部分都是 PowerShell v3.0 和更新版本。
$myObject = [PSCustomObject]@{
Name = 'Kevin'
Language = 'PowerShell'
State = 'Texas'
}
這個方法適用於我,因為我只針對所有專案使用哈希表。 但有時候,我希望 PowerShell 將哈希表視為更像物件。 您首先注意到差異在於當您想要使用 Format-Table
或 Export-CSV
,而且您發現哈希表只是索引鍵/值組的集合。
然後,您可以存取並使用值,就像是一般對象一樣。
$myObject.Name
轉換哈希表
當我在主題上時,您知道您是否可以執行此動作:
$myHashtable = @{
Name = 'Kevin'
Language = 'PowerShell'
State = 'Texas'
}
$myObject = [pscustomobject]$myHashtable
我寧願從頭開始建立物件,但有時候您必須先使用哈希表。 此範例之所以有效,是因為建構函式會取得物件屬性的哈希表。 其中一個重要注意事項是,雖然此方法可運作,但並非完全相同的對等專案。 最大的差異在於不會保留屬性的順序。
如果您想要保留順序,請參閱 已排序的哈希表。
舊版方法
您可能已經看到人們用來 New-Object
建立自定義物件。
$myHashtable = @{
Name = 'Kevin'
Language = 'PowerShell'
State = 'Texas'
}
$myObject = New-Object -TypeName PSObject -Property $myHashtable
這種方式相當慢,但這可能是您在舊版 PowerShell 上的最佳選項。
儲存至檔案
我找到將哈希表儲存至檔案的最佳方式是將它儲存為 JSON。 您可以將它匯回 [PSCustomObject]
$myObject | ConvertTo-Json -depth 1 | Set-Content -Path $Path
$myObject = Get-Content -Path $Path | ConvertFrom-Json
我涵蓋更多將物件儲存至檔案的方法,請參閱 許多讀取和寫入檔案的方式。
使用屬性
新增屬性
您仍然可以使用 Add-Member
將新屬性新增至 。PSCustomObject
$myObject | Add-Member -MemberType NoteProperty -Name 'ID' -Value 'KevinMarquette'
$myObject.ID
拿掉屬性
您也可以從物件中移除屬性。
$myObject.psobject.properties.remove('ID')
.psobject
是內部成員,可讓您存取基底對象元數據。 如需內部成員的詳細資訊,請參閱 about_Intrinsic_Members。
列舉屬性名稱
有時候您需要物件上所有屬性名稱的清單。
$myObject | Get-Member -MemberType NoteProperty | Select -ExpandProperty Name
我們也可以從 psobject
屬性中取得相同的清單。
$myobject.psobject.properties.name
注意
Get-Member
會依字母順序傳回屬性。 使用成員存取運算符來列舉屬性名稱,會依照在 對象上定義的順序傳回屬性。
動態存取屬性
我已提到您可以直接存取屬性值。
$myObject.Name
您可以使用字串作為屬性名稱,而且它仍然可以運作。
$myObject.'Name'
我們可以再執行一個步驟,並針對屬性名稱使用變數。
$property = 'Name'
$myObject.$property
我知道這看起來很奇怪,但它有效。
將 PSCustomObject 轉換成哈希表
若要從最後一節繼續進行,您可以動態地逐步解說屬性,並從中建立哈希表。
$hashtable = @{}
foreach( $property in $myobject.psobject.properties.name )
{
$hashtable[$property] = $myObject.$property
}
測試屬性
如果您需要知道屬性是否存在,您可以只檢查該屬性是否有值。
if( $null -ne $myObject.ID )
但是,如果值可能是 $null
,您可以藉由檢查 是否有它來檢查 psobject.properties
它是否存在。
if( $myobject.psobject.properties.match('ID').Count )
新增物件方法
如果您需要將文稿方法新增至 物件,您可以使用 和 ScriptBlock
來執行它Add-Member
。 您必須使用 this
自動變數參考目前的物件。 以下是 scriptblock
將物件轉換成哈希表的 。 (相同程式代碼形成最後一個範例)
$ScriptBlock = {
$hashtable = @{}
foreach( $property in $this.psobject.properties.name )
{
$hashtable[$property] = $this.$property
}
return $hashtable
}
然後,我們會將它新增至 對象作為腳本屬性。
$memberParam = @{
MemberType = "ScriptMethod"
InputObject = $myobject
Name = "ToHashtable"
Value = $scriptBlock
}
Add-Member @memberParam
然後我們可以像這樣呼叫函式:
$myObject.ToHashtable()
對象與實值類型
對象和實值類型不會以相同方式處理變數指派。 如果您將實值類型指派給彼此,則只會將值複製到新的變數。
$first = 1
$second = $first
$second = 2
在此情況下, $first
是 1,而 $second
是 2。
物件變數會保存實際對象的參考。 當您將一個物件指派給新的變數時,它們仍會參考相同的物件。
$third = [PSCustomObject]@{Key=3}
$fourth = $third
$fourth.Key = 4
因為 $third
和 $fourth
參考物件相同的實例, $third.key
因此 和 $fourth.Key
都是 4。
psobject.copy()
如果您需要對象的真實複本,您可以複製它。
$third = [PSCustomObject]@{Key=3}
$fourth = $third.psobject.copy()
$fourth.Key = 4
複製會建立 對象的淺層複本。 它們現在有不同的實例,而且 $third.key
是 3, $fourth.Key
在此範例中為 4。
我將此稱為淺層複本,因為如果您有巢狀物件(具有屬性的物件包含其他物件),則只會複製最上層值。 子物件會彼此參考。
自定義物件類型的 PSTypeName
既然我們有 對象,我們可以再做一些事情,可能不太明顯。 我們需要做的第一件事是給它一個 PSTypeName
。 這是我看到人們執行此動作的最常見方式:
$myObject.PSObject.TypeNames.Insert(0,"My.Object")
我最近從 Redditor 發現了另一種方法來執行這項操作 u/markekraus
。 他談到這種方法,可讓您內嵌定義它。
$myObject = [PSCustomObject]@{
PSTypeName = 'My.Object'
Name = 'Kevin'
Language = 'PowerShell'
State = 'Texas'
}
我喜歡這多麼適合語言。 既然我們有一個具有適當類型名稱的物件,我們可以做一些更多的事情。
注意
您也可以使用 PowerShell 類別建立自訂 PowerShell 類型。 如需詳細資訊,請參閱 PowerShell類別概觀。
使用 DefaultPropertySet (很長的路要走)
PowerShell 會決定預設要顯示哪些屬性。 許多原生命令都有一個.ps1xml
格式化檔案,可執行所有繁重的工作。 從 Boe Prox 的這篇文章中,我們還有另一種方式可以使用 PowerShell 在自定義物件上執行此動作。 我們可以為它提供一個 MemberSet
供它使用。
$defaultDisplaySet = 'Name','Language'
$defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet',[string[]]$defaultDisplaySet)
$PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet)
$MyObject | Add-Member MemberSet PSStandardMembers $PSStandardMembers
現在,當我的物件剛落到殼層時,它預設只會顯示這些屬性。
使用 DefaultPropertySet 的 Update-TypeData
這很好,但我最近看到使用Update-TypeData 指定預設屬性的更好方式。
$TypeData = @{
TypeName = 'My.Object'
DefaultDisplayPropertySet = 'Name','Language'
}
Update-TypeData @TypeData
這很簡單,如果我沒有這篇文章作為快速參考,我幾乎可以記住它。 現在,我可以輕鬆地建立具有許多屬性的物件,並在從殼層查看它時,提供一個很好的乾淨檢視。 如果我需要存取或查看這些其他屬性,它們仍然存在。
$myObject | Format-List *
使用 ScriptProperty 的 Update-TypeData
我從該影片中得到的其他內容是為您的物件建立腳本屬性。 這是指出這也適用於現有物件的好時機。
$TypeData = @{
TypeName = 'My.Object'
MemberType = 'ScriptProperty'
MemberName = 'UpperCaseName'
Value = {$this.Name.toUpper()}
}
Update-TypeData @TypeData
您可以在建立物件或之後執行此動作,而且它仍可正常運作。 這與搭配腳本屬性使用 Add-Member
不同。 當您使用 Add-Member
我稍早參考的方式時,它只會存在於該物件的特定實例上。 這一個套用至具有這個 TypeName
的所有物件。
函數參數
您現在可以將這些自訂類型用於函式和腳本中的參數。 您可以讓一個函式建立這些自定義物件,然後將它們傳遞至其他函式。
param( [PSTypeName('My.Object')]$Data )
PowerShell 要求對像是您指定的類型。 如果類型不相符,它會擲回驗證錯誤,以將測試步驟儲存在程式碼中。 讓 PowerShell 執行最佳作業的絕佳範例。
函式 OutputType
您也可以定義 OutputType
進階函式的 。
function Get-MyObject
{
[OutputType('My.Object')]
[CmdletBinding()]
param
(
...
OutputType 屬性值只是檔附註。 它不是衍生自函式程序代碼,也不是與實際函式輸出比較。
您會使用輸出類型的主要原因,就是讓函式的中繼資訊反映您的意圖。 例如 Get-Command
, Get-Help
開發環境可以利用的專案。 如果您想要詳細資訊,請查看其說明: about_Functions_OutputTypeAttribute。
如此一來,如果您使用 Pester 來單元測試函式,則驗證輸出物件符合 OutputType 是個好主意。 這可能會攔截那些不應該落到管道的變數。
結語
這一切的內容都是關於 [PSCustomObject]
的,但許多這項資訊適用於一般物件。
我以前在傳遞中看到了大部分的功能,但從未看到它們被呈現為關於 的資訊 PSCustomObject
集合。 就在上周,我偶然發現了另一個,驚訝於我以前沒有見過它。 我想把所有這些想法拉在一起,所以你可以希望看到更大的畫面,並意識到他們,當你有機會使用他們。 我希望您已學到一些東西,並可以找到一種方法,以在您的腳本中運作。