Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
PSCustomObject
è un ottimo strumento da aggiungere alla cintura di strumenti di PowerShell. Si inizieranno con le nozioni di base e si esamineranno le funzionalità più avanzate. L'idea alla base dell'uso di un PSCustomObject
consiste nell'avere un modo semplice per creare dati strutturati. Esaminare il primo esempio e si avrà un'idea migliore di ciò che significa.
Nota
La versione originale di questo articolo è apparsa nel blog scritto da @KevinMarquette. Il team di PowerShell ringrazia Kevin per aver condiviso questo contenuto con noi. Consultare il suo blog all'indirizzo PowerShellExplained.com.
Creazione di un PSCustomObject
Mi piace usare [pscustomobject]
in PowerShell. La creazione di un oggetto utilizzabile non è mai stata più semplice.
Per questo motivo, io vado a ignorare tutti gli altri modi in cui è possibile creare un oggetto, ma devo menzionare che la maggior parte di questi esempi sono PowerShell v3.0 e versioni successive.
$myObject = [pscustomobject]@{
Name = 'Kevin'
Language = 'PowerShell'
State = 'Texas'
}
Questo metodo funziona bene per me perché uso tabelle hash praticamente per tutto. Ma ci sono momenti in cui vorrei che PowerShell consideri le tabelle hash più simili a un oggetto. Il primo aspetto che si nota è quando si vuole usare Format-Table
o Export-Csv
e ci si rende conto che una tabella hash è solo una raccolta di coppie chiave/valore.
È quindi possibile accedere e usare i valori come si farebbe con un oggetto normale.
$myObject.Name
Conversione di una tabella hash
Mentre sono sull'argomento, sapevi che potresti eseguire questa operazione:
$myHashtable = @{
Name = 'Kevin'
Language = 'PowerShell'
State = 'Texas'
}
$myObject = [pscustomobject]$myHashtable
Preferisco creare l'oggetto dall'inizio, ma ci sono volte che devi prima lavorare con una tabella hash. Questo esempio funziona perché il costruttore accetta una tabella hash per le proprietà dell'oggetto. Una nota importante è che, mentre questo metodo funziona, non è un equivalente esatto. La differenza principale è che l'ordine delle proprietà non viene mantenuto.
Se vuoi mantenere l'ordine, vedi Tabelle hash ordinate.
Approccio tradizionale (legacy)
È possibile che gli utenti usino New-Object
per creare oggetti personalizzati.
$myHashtable = @{
Name = 'Kevin'
Language = 'PowerShell'
State = 'Texas'
}
$myObject = New-Object -TypeName psobject -Property $myHashtable
Questo modo è piuttosto lento, ma può essere l'opzione migliore nelle versioni precedenti di PowerShell.
Salvataggio in un file
Trovo il modo migliore per salvare una tabella hash in un file è salvarla come JSON. È possibile importarlo nuovamente in un [pscustomobject]
$myObject | ConvertTo-Json -Depth 1 | Set-Content -Path $Path
$myObject = Get-Content -Path $Path | ConvertFrom-Json
Vengono illustrati altri modi per salvare gli oggetti in un file nell'articolo su I molti modi per leggere e scrivere nei file.
Lavorare con le proprietà
Aggiunta di proprietà
È comunque possibile aggiungere nuove proprietà al PSCustomObject
con Add-Member
.
$myObject | Add-Member -MemberType NoteProperty -Name 'ID' -Value 'KevinMarquette'
$myObject.ID
Rimuovere le proprietà
È anche possibile rimuovere le proprietà da un oggetto .
$myObject.psobject.Properties.Remove('ID')
Il .psobject
è un membro intrinseco che consente di accedere ai metadati dell'oggetto di base. Per altre informazioni sui membri intrinseci, vedere about_Intrinsic_Members.
Enumerazione dei nomi delle proprietà
A volte è necessario un elenco di tutti i nomi delle proprietà in un oggetto .
$myObject | Get-Member -MemberType NoteProperty | select -ExpandProperty Name
È possibile ottenere lo stesso elenco anche dalla proprietà psobject
.
$myobject.psobject.Properties.Name
Nota
Get-Member
restituisce le proprietà in ordine alfabetico. L'utilizzo dell'operatore di accesso ai membri per enumerare i nomi delle proprietà restituisce le proprietà nell'ordine in cui sono state definite nell'oggetto .
Accesso dinamico alle proprietà
Ho già accennato che è possibile accedere direttamente ai valori delle proprietà.
$myObject.Name
È possibile usare una stringa per il nome della proprietà e funzionerà comunque.
$myObject.'Name'
È possibile eseguire questo passaggio e usare una variabile per il nome della proprietà.
$property = 'Name'
$myObject.$property
So che sembra strano, ma funziona.
Convertire PSCustomObject in una tabella hash
Per continuare dall'ultima sezione, è possibile esaminare in modo dinamico le proprietà e creare una tabella hash da essi.
$hashtable = @{}
foreach( $property in $myobject.psobject.Properties.Name )
{
$hashtable[$property] = $myObject.$property
}
Verifica delle proprietà
Se è necessario sapere se esiste una proprietà, è sufficiente verificare che tale proprietà abbia un valore.
if( $null -ne $myObject.ID )
Tuttavia, se il valore potrebbe essere $null
è possibile verificare se esiste controllando il psobject.Properties
per cercarlo.
if( $myobject.psobject.Properties.Match('ID').Count )
Aggiunta di metodi dell'oggetto
Se è necessario aggiungere un metodo script a un oggetto , è possibile farlo con Add-Member
e un ScriptBlock
. È necessario usare la variabile automatica this
riferita all'oggetto corrente. Ecco un scriptblock
per trasformare un oggetto in una tabella hash. (lo stesso codice costituisce l'ultimo esempio)
$ScriptBlock = {
$hashtable = @{}
foreach( $property in $this.psobject.Properties.Name )
{
$hashtable[$property] = $this.$property
}
return $hashtable
}
Quindi lo aggiungiamo all'oggetto come proprietà script.
$memberParam = @{
MemberType = "ScriptMethod"
InputObject = $myobject
Name = "ToHashtable"
Value = $scriptBlock
}
Add-Member @memberParam
È quindi possibile chiamare la funzione come segue:
$myObject.ToHashtable()
Oggetti versus tipi di valore
Gli oggetti e i tipi valore non gestiscono le assegnazioni di variabili nello stesso modo. Se si assegnano tipi valore l'uno all'altro, viene copiato solo il valore nella nuova variabile.
$first = 1
$second = $first
$second = 2
In questo caso, $first
è 1 e $second
è 2.
Le variabili oggetto contengono un riferimento all'oggetto effettivo. Quando si assegna un oggetto a una nuova variabile, fanno comunque riferimento allo stesso oggetto.
$third = [pscustomobject]@{Key=3}
$fourth = $third
$fourth.Key = 4
Poiché $third
e $fourth
fanno riferimento alla stessa istanza di un oggetto, sia $third.key
che $fourth.Key
sono 4.
psobject. Copy()
Se è necessaria una copia vera di un oggetto, è possibile clonarla.
$third = [pscustomobject]@{Key=3}
$fourth = $third.psobject.Copy()
$fourth.Key = 4
Clone crea una copia superficiale dell'oggetto. Ora hanno istanze diverse e $third.key
è 3 e $fourth.Key
è 4 in questo esempio.
Si chiama questa copia superficiale perché se sono presenti oggetti annidati (oggetti con proprietà contengono altri oggetti), vengono copiati solo i valori di primo livello. Gli oggetti figlio fanno riferimento l'uno all'altro.
PSTypeName per i tipi di oggetto personalizzati
Ora che abbiamo un oggetto, ci sono altre cose che possiamo fare con esso che potrebbe non essere quasi ovvio. La prima cosa che dobbiamo fare è dare un PSTypeName
. Questo è il modo più comune in cui vedo le persone che lo fanno:
$myObject.psobject.TypeNames.Insert(0,"My.Object")
Recentemente ho scoperto un altro modo per farlo da Redditor u/markekraus
. Parla di questo approccio che consente di definirlo in linea.
$myObject = [pscustomobject]@{
PSTypeName = 'My.Object'
Name = 'Kevin'
Language = 'PowerShell'
State = 'Texas'
}
Mi piace come questo si inserisce perfettamente nel linguaggio. Ora che è disponibile un oggetto con un nome di tipo appropriato, è possibile eseguire altre operazioni.
Nota
È anche possibile creare tipi di PowerShell personalizzati usando le classi di PowerShell. Per altre informazioni, vedere Panoramica della classe PowerShell.
Uso di DefaultPropertySet (il metodo dettagliato)
PowerShell decide per noi quali proprietà visualizzare per impostazione predefinita. Molti comandi nativi hanno un file di formattazione .ps1xml
che svolge tutte le operazioni di lavoro pesante. Dal post di Boe Prox, c'è un altro modo per effettuare questa operazione sul nostro oggetto personalizzato usando solo PowerShell. Possiamo dargli un MemberSet
per usarlo.
$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
Quando il mio oggetto cade nella shell, mostrerà solo quelle proprietà per impostazione predefinita.
Update-TypeData con DefaultPropertySet
Questo è bello, ma di recente ho visto un modo migliore usando Update-TypeData per specificare le proprietà predefinite.
$TypeData = @{
TypeName = 'My.Object'
DefaultDisplayPropertySet = 'Name','Language'
}
Update-TypeData @TypeData
Questo è abbastanza semplice che potrei quasi ricordarlo se non ho avuto questo post come riferimento rapido. Ora posso creare facilmente oggetti con molte proprietà e dare comunque una bella vista pulita quando lo guardo dalla shell. Se devo accedere o vedere queste altre proprietà, sono ancora lì.
$myObject | Format-List *
Update-TypeData con ScriptProperty
Qualcos'altro che ho imparato da quel video è stato creare proprietà script per gli oggetti. Questo sarebbe un buon momento per sottolineare che funziona anche per gli oggetti esistenti.
$TypeData = @{
TypeName = 'My.Object'
MemberType = 'ScriptProperty'
MemberName = 'UpperCaseName'
Value = {$this.Name.ToUpper()}
}
Update-TypeData @TypeData
È possibile eseguire questa operazione prima che l'oggetto venga creato o dopo e funzionerà comunque. Questo rende questo aspetto diverso rispetto all'uso di Add-Member
con una proprietà script. Quando si usa Add-Member
così come ho spiegato prima, esso esiste solo in quella specifica istanza dell'oggetto. Questo vale per tutti gli oggetti con questo TypeName
.
Parametri della funzione
È ora possibile usare questi tipi personalizzati per i parametri nelle funzioni e negli script. È possibile creare questi oggetti personalizzati e quindi passarli in altre funzioni.
param( [PSTypeName('My.Object')]$Data )
PowerShell richiede che l'oggetto sia il tipo specificato. Genera un errore di convalida se il tipo non viene abbinato automaticamente, evitando così la necessità di testarlo manualmente nel tuo codice. Un ottimo esempio di consentire a PowerShell di eseguire le operazioni migliori.
Funzione OutputType
È anche possibile definire un OutputType
per le funzioni avanzate.
function Get-MyObject
{
[OutputType('My.Object')]
[CmdletBinding()]
param
(
...
Il valore dell'attributo OutputType è solo una nota della documentazione. Non è derivato dal codice della funzione o confrontato con l'output effettivo della funzione.
Il motivo principale per cui si usa un tipo di output è in modo che le meta informazioni sulla funzione riflettano le intenzioni. Elementi come Get-Command
e Get-Help
che l'ambiente di sviluppo può sfruttare. Per ulteriori informazioni, dai un'occhiata all'aiuto per: about_Functions_OutputTypeAttribute.
Detto questo, se si usa Pester per eseguire unit test delle funzioni, è consigliabile convalidare gli oggetti di output corrispondenti al OutputType. Questo potrebbe intercettare le variabili che semplicemente rientrano nella pipeline quando non è previsto.
Conclusioni
Il contesto di tutto ciò riguardava [pscustomobject]
, ma molte di queste informazioni valgono per gli oggetti in generale.
Ho visto la maggior parte di queste caratteristiche passando prima, ma non li ho mai visti presentati come una raccolta di informazioni su PSCustomObject
. Proprio questa settimana scorsa mi sono imbattuto in un altro e sono rimasto sorpreso che non l'avevo vista prima. Volevo mettere insieme tutte queste idee in modo da poter vedere l'immagine più grande e essere consapevole di loro quando hai l'opportunità di usarle. Spero che tu abbia imparato qualcosa e che tu possa trovare un modo per integrare questo nei tuoi script.