您想要瞭解 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-TableExport-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")

我最近發現了另一種方法,從這篇文章 中由 /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-CommandGet-Help 開發環境可以利用的專案。 如果您想要詳細資訊,請查看其說明: about_Functions_OutputTypeAttribute

如此一來,如果您使用 Pester 來單元測試函式,則驗證輸出物件符合 OutputType 是個好主意。 這可能會攔截那些不應該落到管道的變數。

結語

這一切的內容都是關於 [PSCustomObject]的,但許多這項資訊適用於一般物件。

我以前在傳遞中看到了大部分的功能,但從未看到它們被呈現為關於 的資訊 PSCustomObject集合。 就在上周,我偶然發現了另一個,驚訝於我以前沒有見過它。 我想把所有這些想法拉在一起,所以你可以希望看到更大的畫面,並意識到他們,當你有機會使用他們。 我希望您已學到一些東西,並可以找到一種方法,以在您的腳本中運作。