Alles wat u wilde weten over PSCustomObject
PSCustomObject
is een uitstekend hulpmiddel om toe te voegen aan uw PowerShell-gereedschapsgordel. Laten we beginnen met de basisbeginselen en onze weg in de geavanceerdere functies. Het idee achter het gebruik van een PSCustomObject
is om een eenvoudige manier te hebben om gestructureerde gegevens te maken. Bekijk het eerste voorbeeld en u hebt een beter idee van wat dat betekent.
Notitie
De oorspronkelijke versie van dit artikel verscheen op het blog dat is geschreven door @KevinMarquette. Het PowerShell-team bedankt Kevin voor het delen van deze inhoud met ons. Bekijk zijn blog op PowerShellExplained.com.
Een PSCustomObject maken
Ik gebruik [PSCustomObject]
graag in PowerShell. Het maken van een bruikbaar object is nog nooit zo eenvoudig geweest.
Daarom ga ik alle andere manieren overslaan waarop u een object kunt maken, maar ik moet vermelden dat de meeste van deze voorbeelden PowerShell v3.0 en hoger zijn.
$myObject = [PSCustomObject]@{
Name = 'Kevin'
Language = 'PowerShell'
State = 'Texas'
}
Deze methode werkt goed voor mij omdat ik hashtables voor bijna alles gebruik. Maar soms wil ik dat PowerShell hashtables meer als een object behandelt. De eerste plaats waar u het verschil ziet, is wanneer u wilt gebruiken Format-Table
of Export-CSV
als u beseft dat een hashtabel slechts een verzameling sleutel-/waardeparen is.
Vervolgens kunt u de waarden openen en gebruiken zoals u een normaal object zou doen.
$myObject.Name
Een hashtabel converteren
Terwijl ik in het onderwerp zit, wist je dat je dit zou kunnen doen:
$myHashtable = @{
Name = 'Kevin'
Language = 'PowerShell'
State = 'Texas'
}
$myObject = [pscustomobject]$myHashtable
Ik wil het object liever vanaf het begin maken, maar er zijn tijden dat u eerst met een hashtabel moet werken. Dit voorbeeld werkt omdat de constructor een hashtabel gebruikt voor de objecteigenschappen. Een belangrijke opmerking is dat hoewel deze methode werkt, het geen exacte equivalent is. Het grootste verschil is dat de volgorde van de eigenschappen niet behouden blijft.
Zie Geordende hashtabellen als u de volgorde wilt behouden.
Verouderde benadering
Mogelijk hebt u gezien dat mensen aangepaste New-Object
objecten maken.
$myHashtable = @{
Name = 'Kevin'
Language = 'PowerShell'
State = 'Texas'
}
$myObject = New-Object -TypeName PSObject -Property $myHashtable
Op deze manier is het wat langzamer, maar het is mogelijk uw beste optie voor vroege versies van PowerShell.
Opslaan in een bestand
Ik vind de beste manier om een hashtabel op te slaan in een bestand is het opslaan als JSON. U kunt het terug importeren in een [PSCustomObject]
$myObject | ConvertTo-Json -depth 1 | Set-Content -Path $Path
$myObject = Get-Content -Path $Path | ConvertFrom-Json
Ik beschrijf meer manieren om objecten op te slaan in een bestand in mijn artikel over de vele manieren om bestanden te lezen en te schrijven.
Werken met eigenschappen
Eigenschappen toevoegen
U kunt nog steeds nieuwe eigenschappen aan uw PSCustomObject
toevoegen.Add-Member
$myObject | Add-Member -MemberType NoteProperty -Name 'ID' -Value 'KevinMarquette'
$myObject.ID
Eigenschappen verwijderen
U kunt ook eigenschappen van een object verwijderen.
$myObject.psobject.properties.remove('ID')
Het .psobject
is een intrinsiek lid dat u toegang geeft tot metagegevens van basisobjecten. Zie about_Intrinsic_Members voor meer informatie over intrinsieke leden.
Eigenschapsnamen opsommen
Soms hebt u een lijst met alle eigenschapsnamen voor een object nodig.
$myObject | Get-Member -MemberType NoteProperty | Select -ExpandProperty Name
We kunnen dezelfde lijst ook van de psobject
accommodatie ophalen.
$myobject.psobject.properties.name
Notitie
Get-Member
retourneert de eigenschappen in alfabetische volgorde. Als u de operator voor lidtoegang gebruikt om de eigenschapsnamen op te sommen, worden de eigenschappen geretourneerd in de volgorde waarin ze zijn gedefinieerd voor het object.
Dynamisch toegang tot eigenschappen
Ik heb al gezegd dat u rechtstreeks toegang hebt tot eigenschapswaarden.
$myObject.Name
U kunt een tekenreeks gebruiken voor de naam van de eigenschap en deze werkt nog steeds.
$myObject.'Name'
We kunnen deze stap nog één stap uitvoeren en een variabele gebruiken voor de naam van de eigenschap.
$property = 'Name'
$myObject.$property
Ik weet dat dat er vreemd uitziet, maar het werkt wel.
PSCustomObject converteren naar een hashtabel
Als u verder wilt gaan vanuit de laatste sectie, kunt u de eigenschappen dynamisch doorlopen en er een hashtabel van maken.
$hashtable = @{}
foreach( $property in $myobject.psobject.properties.name )
{
$hashtable[$property] = $myObject.$property
}
Testen op eigenschappen
Als u wilt weten of er een eigenschap bestaat, kunt u gewoon controleren of die eigenschap een waarde heeft.
if( $null -ne $myObject.ID )
Maar als de waarde kan zijn $null
, kunt u controleren of deze bestaat door de psobject.properties
waarde te controleren.
if( $myobject.psobject.properties.match('ID').Count )
Objectmethoden toevoegen
Als u een scriptmethode aan een object wilt toevoegen, kunt u dit doen met Add-Member
en een ScriptBlock
. U moet de this
automatische variabele naar het huidige object verwijzen. Hier volgt een scriptblock
om een object om te zetten in een hashtabel. (dezelfde code vormt het laatste voorbeeld)
$ScriptBlock = {
$hashtable = @{}
foreach( $property in $this.psobject.properties.name )
{
$hashtable[$property] = $this.$property
}
return $hashtable
}
Vervolgens voegen we het toe aan ons object als een scripteigenschap.
$memberParam = @{
MemberType = "ScriptMethod"
InputObject = $myobject
Name = "ToHashtable"
Value = $scriptBlock
}
Add-Member @memberParam
Vervolgens kunnen we onze functie als volgt aanroepen:
$myObject.ToHashtable()
Objecten versus waardetypen
Objecten en waardetypen verwerken variabelentoewijzingen niet op dezelfde manier. Als u waardetypen aan elkaar toewijst, wordt alleen de waarde gekopieerd naar de nieuwe variabele.
$first = 1
$second = $first
$second = 2
In dit geval $first
is dit 1 en $second
2.
Objectvariabelen bevatten een verwijzing naar het werkelijke object. Wanneer u één object aan een nieuwe variabele toewijst, verwijzen ze nog steeds naar hetzelfde object.
$third = [PSCustomObject]@{Key=3}
$fourth = $third
$fourth.Key = 4
Omdat $third
en $fourth
verwijst naar hetzelfde exemplaar van een object, beide $third.key
en $fourth.Key
zijn ze 4.
psobject.copy()
Als u een echte kopie van een object nodig hebt, kunt u het klonen.
$third = [PSCustomObject]@{Key=3}
$fourth = $third.psobject.copy()
$fourth.Key = 4
Kloon maakt een ondiepe kopie van het object. Ze hebben nu verschillende exemplaren en $third.key
zijn 3 en $fourth.Key
zijn 4 in dit voorbeeld.
Ik noem dit een ondiepe kopie omdat als u geneste objecten hebt (objecten met eigenschappen andere objecten bevatten), alleen de waarden op het hoogste niveau worden gekopieerd. De onderliggende objecten verwijzen naar elkaar.
PSTypeName voor aangepaste objecttypen
Nu we een object hebben, zijn er nog een paar dingen die we ermee kunnen doen die misschien niet zo duidelijk zijn. Het eerste wat we moeten doen, is het geven van een PSTypeName
. Dit is de meest voorkomende manier waarop ik zie dat mensen het doen:
$myObject.PSObject.TypeNames.Insert(0,"My.Object")
Ik ontdekte onlangs een andere manier om dit te doen van Redditor u/markekraus
. Hij praat over deze aanpak waarmee u deze inline kunt definiëren.
$myObject = [PSCustomObject]@{
PSTypeName = 'My.Object'
Name = 'Kevin'
Language = 'PowerShell'
State = 'Texas'
}
Ik hou ervan hoe mooi dit past in de taal. Nu we een object met een juiste typenaam hebben, kunnen we nog wat meer dingen doen.
Notitie
U kunt ook aangepaste PowerShell-typen maken met behulp van PowerShell-klassen. Zie Overzicht van PowerShell-klassen voor meer informatie.
DefaultPropertySet gebruiken (de lange weg)
PowerShell bepaalt welke eigenschappen standaard moeten worden weergegeven. Veel van de systeemeigen opdrachten hebben een .ps1xml
opmaakbestand dat al het zware werk doet. Vanuit dit bericht van Boe Prox is er een andere manier om dit te doen op ons aangepaste object met behulp van alleen PowerShell. We kunnen het MemberSet
gebruiken.
$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
Als mijn object nu alleen in de shell valt, worden deze eigenschappen standaard alleen weergegeven.
Update-TypeData met DefaultPropertySet
Dit is handig, maar ik zag onlangs een betere manier om Update-TypeData te gebruiken om de standaardeigenschappen op te geven.
$TypeData = @{
TypeName = 'My.Object'
DefaultDisplayPropertySet = 'Name','Language'
}
Update-TypeData @TypeData
Dat is eenvoudig genoeg dat ik het bijna kon onthouden als ik dit bericht niet als snelzoekbericht had. Nu kan ik eenvoudig objecten met veel eigenschappen maken en het nog steeds een mooie schone weergave geven wanneer ik het bekijk vanuit de shell. Als ik die andere eigenschappen moet openen of zien, zijn ze er nog.
$myObject | Format-List *
Update-TypeData met ScriptProperty
Iets anders dat ik uit die video heb gekregen, was het maken van scripteigenschappen voor uw objecten. Dit is een goed moment om aan te geven dat dit ook werkt voor bestaande objecten.
$TypeData = @{
TypeName = 'My.Object'
MemberType = 'ScriptProperty'
MemberName = 'UpperCaseName'
Value = {$this.Name.toUpper()}
}
Update-TypeData @TypeData
U kunt dit doen voordat uw object wordt gemaakt of daarna en het nog steeds werkt. Dit is wat dit anders maakt dan het gebruik met Add-Member
een scripteigenschap. Wanneer u Add-Member
de manier gebruikt waarnaar ik eerder heb verwezen, bestaat deze alleen op dat specifieke exemplaar van het object. Deze is van toepassing op alle objecten met deze TypeName
.
Functieparameters
U kunt deze aangepaste typen nu gebruiken voor parameters in uw functies en scripts. U kunt met één functie deze aangepaste objecten maken en deze vervolgens doorgeven aan andere functies.
param( [PSTypeName('My.Object')]$Data )
PowerShell vereist dat het object het type is dat u hebt opgegeven. Er wordt een validatiefout gegenereerd als het type niet automatisch overeenkomt om de stap voor het testen ervan in uw code op te slaan. Een goed voorbeeld van het laten doen van PowerShell wat het beste doet.
Function OutputType
U kunt ook een OutputType
voor uw geavanceerde functies definiëren.
function Get-MyObject
{
[OutputType('My.Object')]
[CmdletBinding()]
param
(
...
De waarde van het kenmerk OutputType is alleen een documentatienotitie. Het is niet afgeleid van de functiecode of vergeleken met de werkelijke functie-uitvoer.
De belangrijkste reden waarom u een uitvoertype zou gebruiken, is dat metagegevens over uw functie uw intenties weerspiegelen. Dingen zoals Get-Command
en Get-Help
die uw ontwikkelomgeving kan profiteren van. Als u meer informatie wilt, bekijkt u de Help voor deze informatie: about_Functions_OutputTypeAttribute.
Als u Pester gebruikt om uw functies te testen, is het een goed idee om de uitvoerobjecten te valideren die overeenkomen met uw OutputType. Dit kan variabelen vangen die gewoon in de pijp vallen als dat niet het geval is.
Gedachten sluiten
De context hiervan ging allemaal om [PSCustomObject]
, maar veel van deze informatie is van toepassing op objecten in het algemeen.
Ik heb de meeste van deze functies al eerder gezien, maar ze nooit gezien als een verzameling informatie over PSCustomObject
. Alleen deze vorige week struikelde ik tegen een andere en was verbaasd dat ik het nog niet eerder had gezien. Ik wilde al deze ideeën samenbrengen, zodat je hopelijk het grotere plaatje kunt zien en er op de hoogte van bent wanneer je de kans hebt om ze te gebruiken. Ik hoop dat u iets hebt geleerd en een manier kunt vinden om dit in uw scripts te doen.