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 -Name ID
-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
的特征,但是就在上周我見到一個,令我非常驚嘆並非非常吃驚我從來沒有看到過它.因此我決定提取它的主要主要思想放在一起寫成這篇博客以其它你可以看到它更大的藍圖並且在使用到的時候有所警覺,我希望你能學到東西並且把它用到的自己的腳本里