Allt du ville veta om PSCustomObject
PSCustomObject
är ett bra verktyg att lägga till i ditt PowerShell-verktygsbälte. Vi börjar med grunderna och arbetar oss in i de mer avancerade funktionerna. Tanken med att använda en PSCustomObject
är att ha ett enkelt sätt att skapa strukturerade data. Ta en titt på det första exemplet så får du en bättre uppfattning om vad det innebär.
Anmärkning
Den ursprungliga versionen av den här artikeln publicerades på bloggen som skrevs av @KevinMarquette. PowerShell-teamet tackar Kevin för att ha delat det här innehållet med oss. Kolla in hans blogg på PowerShellExplained.com.
Skapa en PSCustomObject
Jag älskar att använda [pscustomobject]
i PowerShell. Det har aldrig varit enklare att skapa ett användbart objekt.
På grund av det kommer jag att hoppa över alla andra sätt du kan skapa ett objekt, men jag måste nämna att de flesta av dessa exempel är PowerShell v3.0 och senare.
$myObject = [pscustomobject]@{
Name = 'Kevin'
Language = 'PowerShell'
State = 'Texas'
}
Den här metoden fungerar bra för mig eftersom jag använder hashtables för nästan allt. Men det finns tillfällen då jag skulle vilja att PowerShell behandlar hashtables mer som ett objekt. Det första du märker är när du vill använda Format-Table
eller Export-Csv
och du inser att en hashtable bara är en samling nyckel/värde-par.
Du kan sedan komma åt och använda värdena som du skulle göra med ett vanligt objekt.
$myObject.Name
Konvertera en hashtabell
Medan jag är på ämnet, visste du att du kunde göra detta:
$myHashtable = @{
Name = 'Kevin'
Language = 'PowerShell'
State = 'Texas'
}
$myObject = [pscustomobject]$myHashtable
Jag föredrar att skapa objektet från början, men det finns tillfällen då man måste arbeta med en hashtabell först. Det här exemplet fungerar eftersom konstruktorn tar en hash-tabell för objektegenskaperna. En viktig sak är att även om den här metoden fungerar är den inte en exakt motsvarighet. Den största skillnaden är att ordningen på egenskaperna inte bevaras.
Om du vill bevara ordningen kan du läsa Ordnade hashtables.
Äldre metod
Du kanske har sett personer använda New-Object
för att skapa anpassade objekt.
$myHashtable = @{
Name = 'Kevin'
Language = 'PowerShell'
State = 'Texas'
}
$myObject = New-Object -TypeName psobject -Property $myHashtable
Detta sätt är ganska lite långsammare men det kan vara ditt bästa alternativ på tidiga versioner av PowerShell.
Spara till en fil
Jag hittar det bästa sättet att spara en hashtable i en fil är att spara den som JSON. Du kan importera tillbaka den till en [pscustomobject]
$myObject | ConvertTo-Json -Depth 1 | Set-Content -Path $Path
$myObject = Get-Content -Path $Path | ConvertFrom-Json
Jag beskriver fler sätt att spara objekt i en fil i min artikel på De många sätten att läsa och skriva till filer.
Arbeta med egenskaper
Lägga till egenskaper
Du kan fortfarande lägga till nya egenskaper i PSCustomObject
med Add-Member
.
$myObject | Add-Member -MemberType NoteProperty -Name 'ID' -Value 'KevinMarquette'
$myObject.ID
Ta bort egenskaper
Du kan också ta bort egenskaper från ett objekt.
$myObject.psobject.Properties.Remove('ID')
.psobject
är en inbyggd medlem som ger dig åtkomst till basobjektmetadata. Mer information om inbyggda medlemmar finns i about_Intrinsic_Members.
Räkna upp egenskapsnamn
Ibland behöver du en lista över alla egenskapsnamn på ett objekt.
$myObject | Get-Member -MemberType NoteProperty | select -ExpandProperty Name
Vi kan också hämta samma lista från egenskapen psobject
.
$myobject.psobject.Properties.Name
Anmärkning
Get-Member
returnerar egenskaperna i alfabetisk ordning. Om du använder operatorn för medlemsåtkomst för att räkna upp egenskapsnamnen returneras egenskaperna i den ordning de definierades för objektet.
Dynamisk åtkomst till egenskaper
Jag har redan nämnt att du kan komma åt egenskapsvärden direkt.
$myObject.Name
Du kan använda en sträng för egenskapsnamnet och det fungerar fortfarande.
$myObject.'Name'
Vi kan ta det här steget till och använda en variabel för egenskapsnamnet.
$property = 'Name'
$myObject.$property
Jag vet att det ser konstigt ut, men det fungerar.
Konvertera PSCustomObject till en hashtable
För att fortsätta från det sista avsnittet kan du dynamiskt genomgå egenskaperna och skapa en hashtabell från dem.
$hashtable = @{}
foreach( $property in $myobject.psobject.Properties.Name )
{
$hashtable[$property] = $myObject.$property
}
Testning för egenskaper
Om du behöver veta om det finns en egenskap kan du bara kontrollera om egenskapen har ett värde.
if( $null -ne $myObject.ID )
Men om värdet kan vara $null
kan du kontrollera om det finns genom att kontrollera psobject.Properties
för det.
if( $myobject.psobject.Properties.Match('ID').Count )
Lägga till objektmetoder
Om du behöver lägga till en skriptmetod i ett objekt kan du göra det med Add-Member
och en ScriptBlock
. Du måste använda den automatiska variabeln this
, som refererar till det aktuella objektet. Här är en scriptblock
för att omvandla ett objekt till en hashtable. (samma kod utgör det sista exemplet)
$ScriptBlock = {
$hashtable = @{}
foreach( $property in $this.psobject.Properties.Name )
{
$hashtable[$property] = $this.$property
}
return $hashtable
}
Sedan lägger vi till det i objektet som en skriptegenskap.
$memberParam = @{
MemberType = "ScriptMethod"
InputObject = $myobject
Name = "ToHashtable"
Value = $scriptBlock
}
Add-Member @memberParam
Sedan kan vi anropa vår funktion så här:
$myObject.ToHashtable()
Objekt jämfört med värdetyper
Objekt och värdetyper hanterar inte variabeltilldelningar på samma sätt. Om du tilldelar värdetyper till varandra kopieras bara värdet till den nya variabeln.
$first = 1
$second = $first
$second = 2
I det här fallet är $first
1 och $second
är 2.
Objektvariabler innehåller en referens till det faktiska objektet. När du tilldelar ett objekt till en ny variabel refererar de fortfarande till samma objekt.
$third = [pscustomobject]@{Key=3}
$fourth = $third
$fourth.Key = 4
Eftersom $third
och $fourth
refererar till samma instans av ett objekt är både $third.key
och $fourth.Key
4.
psobject. Copy()
Om du behöver en sann kopia av ett objekt kan du klona det.
$third = [pscustomobject]@{Key=3}
$fourth = $third.psobject.Copy()
$fourth.Key = 4
Klon skapar en ytlig kopia av objektet. De har olika instanser nu och $third.key
är 3 och $fourth.Key
är 4 i det här exemplet.
Jag kallar detta en ytlig kopia eftersom om du har kapslade objekt (objekt med egenskaper innehåller andra objekt) kopieras endast de översta värdena. Barnobjekten refererar till varandra.
PSTypeName för anpassade objekttyper
Nu när vi har ett objekt finns det några fler saker vi kan göra med det som kanske inte är lika uppenbart. Det första vi behöver göra är att tilldela det ett PSTypeName
. Detta är det vanligaste sättet jag ser människor göra det:
$myObject.psobject.TypeNames.Insert(0,"My.Object")
Jag upptäckte nyligen ett annat sätt att göra detta från Redditor u/markekraus
. Han talar om den här metoden som gör att du kan definiera den direkt.
$myObject = [pscustomobject]@{
PSTypeName = 'My.Object'
Name = 'Kevin'
Language = 'PowerShell'
State = 'Texas'
}
Jag älskar hur fint detta bara passar in i språket. Nu när vi har ett objekt med rätt typnamn kan vi göra några fler saker.
Anmärkning
Du kan också skapa anpassade PowerShell-typer med hjälp av PowerShell-klasser. Mer information finns i PowerShell-klassöversikt.
Använda DefaultPropertySet (det långa sättet)
PowerShell bestämmer vilka egenskaper som ska visas som standard. Många av de interna kommandona har en .ps1xml
formateringsfil som utför alla tunga lyft. Från inlägget av Boe Proxfinns ett annat sätt för oss att göra det här på vårt anpassade objekt, enbart med PowerShell. Vi kan ge den en MemberSet
som den kan använda.
$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
Nu när mitt objekt bara hamnar i skalet, kommer det som standard endast att visa dessa egenskaper.
Update-TypeData med DefaultPropertySet
Detta är trevligt men jag såg nyligen ett bättre sätt att använda Update-TypeData för att ange standardegenskaperna.
$TypeData = @{
TypeName = 'My.Object'
DefaultDisplayPropertySet = 'Name','Language'
}
Update-TypeData @TypeData
Det är enkelt nog att jag nästan kunde komma ihåg det om jag inte hade det här inlägget som en snabb referens. Nu kan jag enkelt skapa objekt med många egenskaper och ändå ge det en fin ren vy när man tittar på det från skalet. Om jag behöver komma åt eller se de andra egenskaperna finns de fortfarande där.
$myObject | Format-List *
Update-TypeData med ScriptProperty
Något annat jag fick ut av videon var att skapa skriptegenskaper för dina objekt. Detta skulle vara ett bra tillfälle att påpeka att detta fungerar även för befintliga objekt.
$TypeData = @{
TypeName = 'My.Object'
MemberType = 'ScriptProperty'
MemberName = 'UpperCaseName'
Value = {$this.Name.ToUpper()}
}
Update-TypeData @TypeData
Du kan göra detta innan objektet skapas eller efter och det fungerar fortfarande. Det är det som gör det annorlunda än att använda Add-Member
med en skriptegenskap. När du använder Add-Member
som jag refererade till tidigare finns den bara på den specifika instansen av objektet. Den här gäller för alla objekt med den här TypeName
.
Funktionsparametrar
Du kan nu använda dessa anpassade typer för parametrar i dina funktioner och skript. Du kan låta en funktion skapa dessa anpassade objekt och sedan skicka dem till andra funktioner.
param( [PSTypeName('My.Object')]$Data )
PowerShell kräver att objektet är den typ som du har angett. Det utlöser ett valideringsfel om typen inte matchar automatiskt, så att du slipper testa det i din kod. Ett bra exempel på hur du låter PowerShell göra det som är bäst.
Funktionsutdatatyp
Du kan också definiera en OutputType
för dina avancerade funktioner.
function Get-MyObject
{
[OutputType('My.Object')]
[CmdletBinding()]
param
(
...
Attributet OutputType är bara en dokumentationsanteckning. Den härleds inte från funktionskoden eller jämförs med de faktiska funktionsutdata.
Den främsta anledningen till att du använder en utdatatyp är att metainformation om din funktion återspeglar dina avsikter. Saker som Get-Command
och Get-Help
som utvecklingsmiljön kan dra nytta av. Om du vill ha mer information kan du ta en titt på hjälpen för den: about_Functions_OutputTypeAttribute.
Med det sagt, om du använder Pester för att enhetstesta dina funktioner skulle det vara en bra idé att verifiera att utdataobjekten matchar din OutputType-. Detta kan fånga variabler som bara trillar ner i röret när de inte borde.
Avslutande tankar
Kontexten för detta handlade om [pscustomobject]
, men mycket av den här informationen gäller för objekt i allmänhet.
Jag har sett de flesta av dessa funktioner i förbigående tidigare men aldrig sett dem presenteras som en samling av information om PSCustomObject
. Så sent som i förra veckan snubblade jag på en annan och blev förvånad över att jag inte hade sett den tidigare. Jag ville samla alla dessa idéer så att du förhoppningsvis kan se helheten och vara medveten om dem när du har möjlighet att använda dem. Jag hoppas att du har lärt dig något och kan hitta ett sätt att arbeta detta i dina skript.