Powershell:關於PSCustomObject你想知道的一切(譯)
PSCustomObject
是Powershell裡非常重要的一個工具,我們先從基礎開始然後再循序漸進講到一些更高階的話題.PSCustomObject
旨在於用簡單的方法來建立結構化資料.下面的第一個例子全讓你更清楚地理解這句話是什麼意思.
建立一個PSCustomObject
在Powershell程式設計裡,我非常喜歡使用[PSCustomObject]
,建立一個可用的物件從來沒有如此簡單.因此,這裡我將跳過使用其它方式來建立一個物件(僅使用PSCustomObject
),要注意的是,需要使用powershell 3.0或者以上的版本.
$myObject = [PSCustomObject]@{ Name= 'Kevin' Language = 'Powershell' State= 'Texas' } $myObject = [pscustomobject]$myHashtable
我很喜歡用原生方法建立一個物件但是有些時候我必須首先先建立一個hashtable.因為PSCustomObject的建構函式要以hashtable裡的屬性作為引數.有一點需要注意的是構建成PSCustomObject後,新物件的屬性順序可能與原來的hashtable不一樣了(物件屬性的順序不再保留).
使用傳統方法建立PSCustomObject
你可能已經看到有人使用New-Object
來建立一個自定義Powershell物件.
$myHashtable = @{ Name= 'Kevin' Language = 'Powershell' State= 'Texas' } $myObject = New-Object -TypeName PSObject -Property $myHashtable
這種方法可能效率低一些但是在早期的Powershell版本中,這可能是最好的選擇了.
儲存到檔案
我發現把hashtable儲存到檔案的最簡單方法是把它儲存為json,然後你可以儲存的json再匯入轉為一個[PSCusomObject]
物件
$myObject | ConvertTo-Json -depth 1- | Set-Content -Path $Path $myObject = Get-Content -Path $Path | ConvertFrom-Json
新增屬性
你可以通過Add-Member
來給PSCustomObject
新增屬性
$myObject | Add-Member -MemberType NoteProperty -NameID
-Value 'KevinMarquette'
$myObject.ID
刪除屬性
你也可以刪除一個物件的屬性
$myObject | Get-Member -MemberType NoteProperty | Select -ExpandProperty Name
psobject
是物件的隱藏屬性,用於獲取底層物件的元資訊
列舉屬性名稱
有時候你需要獲取物件的所有屬性名稱
$myObject | Get-Member -MemberType NoteProperty | Select -ExpandProperty Name
你也可以通過psobject
屬性獲取物件屬性名稱列表
$myobject.psobject.properties.name
動態獲取屬性
我已經提到過你可以直接獲取屬性的值
$myObject.Name
你也可以使用字串作為屬性名稱來獲取,這仍然是有效的
$myObject.'Name'
再進一步,我們可以存一個變數,然後使用它來獲取屬性值
$property = 'Name' $myObject.$property
我知道這看起來有些奇怪,但是它是可以工作的.
把PsCustomObject轉為hashtable
從上一節繼續,你可以動態獲取pscustomobject
物件的屬性,然後用它們建立一個hashtable
$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') )
新增物件方法
如果你想新增指令碼方法到一個物件,你可以通過Add-Member
來新增一個指令碼塊.你需要使用$this
自動變數來引用當前物件.這裡是一個程式碼塊來讓一個pscustomobject
轉換為hashtable
$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")
最近我又發現可以使用以下內聯的方法來實現
$myObject = [PSCustomObject]@{ PSTypeName = 'My.Object' Name= 'Kevin' Language= 'Powershell' State= 'Texas' }
我非常喜歡這種方法.現在我們有了一個合適的型別名稱,我們可以做更多的事情.
使用預設物件屬性集(DefaultPropertySet)
Powershell預設情況下決定幫我們顯示哪些屬性(譯者注:很多物件的屬性有很多,展示出來的只是少部分).很多內建命令都有一個.ps1xml
檔案來做這件事.這裡還有一個直接通過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
以上已經非常nice了,我發現有人用更nice的方法來實現這一功能,那就是使用Update-TypeData
來指定預設屬性
$TypeData = @{ TypeName = 'My.Object' DefaultDisplayPropertySet = 'Name','Language' } Update-TypeData @TypeData
現在我們可以建立一個有很多屬性的物件但是仍然以一種非常簡潔的方法在powershell裡來展示它.如果我們想檢視其它的屬性,它們仍然存在的
$myObject | Format-List *
ScriptProperty的Update-TypeData
對指令碼屬性(ScriptProperty)使用Update-TypeData
$TypeData = @{ TypeName = 'My.Object' MemberType = 'ScriptProperty' MemberName = 'UpperCaseName' Value = {$this.Name.toUpper()} } Update-TypeData @TypeData
你可以在物件建立之前或者之後來做這些,都是可以的.這就是與前面對指令碼塊使用Add-Member
不同的地方.當你像前面一樣使用Add-Member
,它僅僅對物件的某一例項有效.而這裡對整個物件有效.
函式引數
至此你可以對函式或者指令碼使用這些自定義型別.你可以在一個函式裡面建立它們然後在其它函式裡面使用.
param( [PSTypeName('My.Object')]$Data )
powershell會要求你輸入的物件型別符合你宣告的型別.如果型別不符合會丟擲一個驗證錯誤.這是一個很好的讓powershell做它應該做的事情
函式的OutputType
你可以為你的高階函式定義一個OutputType
function Get-MyObject { [OutputType('My.Object')] [CmdletBinding()] param ( ...
OutputType
僅僅是一個文件註解.它並不是從函式程式碼中衍生,也不與函式輸出結果進行比對
使用OutputType
的主要原因是是因為元資料資訊反映了你設計函式的真實意圖.類似像Get-Command
,Get-Help
和你的開發環境可以利用這些元資訊.
也就是說,比如你使用Pester
(譯者注,pester為一個非常流行的powershell單元測試框架)來測試你的函式.確保函式的輸出物件與你的OutputType
物件相吻合是一個很好的做法.它能夠幫你捕獲本不應該出現但是出現的變數
後記
這是一篇關於PSCustomObject
的部落格,但是很多東西都對一般普通物件仍然適用.
過去我見到過提到的大部分特徵但是從來沒有見到過它們完整的在一起來展示PSCustomObject
的特徵,但是就在上週我見到一個,令我非常驚歎並非非常吃驚我從來沒有看到過它.因此我決定提取它的主要主要思想放在一起寫成這篇部落格以其它你可以看到它更大的藍圖並且在使用到的時候有所警覺,我希望你能學到東西並且把它用到的自己的腳本里