Tudo o que você queria saber sobre PSCustomObject
PSCustomObject
é uma ótima ferramenta para adicionar ao seu cinto de ferramentas do PowerShell. Vamos começar com o básico e trabalhar nosso caminho para os recursos mais avançados. A ideia por trás do uso de um PSCustomObject
é ter uma maneira simples de criar dados estruturados. Dê uma olhada no primeiro exemplo e você terá uma ideia melhor do que isso significa.
Nota
A versão original deste artigo apareceu no blog escrito por @KevinMarquette. A equipe do PowerShell agradece Kevin por compartilhar esse conteúdo conosco. Por favor, confira seu blog em PowerShellExplained.com.
Criando um PSCustomObject
Adoro usar [PSCustomObject]
no PowerShell. Criar um objeto utilizável nunca foi tão fácil.
Por isso, vou ignorar todas as outras maneiras de criar um objeto, mas preciso mencionar que a maioria desses exemplos são PowerShell v3.0 e mais recentes.
$myObject = [PSCustomObject]@{
Name = 'Kevin'
Language = 'PowerShell'
State = 'Texas'
}
Este método funciona bem para mim porque eu uso hashtables para praticamente tudo. Mas há momentos em que gostaria que o PowerShell tratasse as hashtables mais como um objeto. O primeiro lugar em que você percebe a diferença é quando você quer usar Format-Table
ou Export-CSV
e você percebe que uma hashtable é apenas uma coleção de pares chave/valor.
Em seguida, você pode acessar e usar os valores como faria com um objeto normal.
$myObject.Name
Convertendo uma hashtable
Enquanto estou no tópico, você sabia que poderia fazer isso:
$myHashtable = @{
Name = 'Kevin'
Language = 'PowerShell'
State = 'Texas'
}
$myObject = [pscustomobject]$myHashtable
Eu prefiro criar o objeto desde o início, mas há momentos em que você tem que trabalhar com uma hashtable primeiro. Este exemplo funciona porque o construtor usa um hashtable para as propriedades do objeto. Uma observação importante é que, embora esse método funcione, ele não é um equivalente exato. A maior diferença é que a ordem dos imóveis não é preservada.
Se quiser preservar a ordem, consulte Tabelas de hash ordenadas.
Abordagem herdada
Você já deve ter visto as pessoas usarem New-Object
para criar objetos personalizados.
$myHashtable = @{
Name = 'Kevin'
Language = 'PowerShell'
State = 'Texas'
}
$myObject = New-Object -TypeName PSObject -Property $myHashtable
Essa maneira é um pouco mais lenta, mas pode ser sua melhor opção nas versões anteriores do PowerShell.
Guardar num ficheiro
Eu acho que a melhor maneira de salvar uma hashtable em um arquivo é salvá-lo como JSON. Você pode importá-lo de volta para um [PSCustomObject]
$myObject | ConvertTo-Json -depth 1 | Set-Content -Path $Path
$myObject = Get-Content -Path $Path | ConvertFrom-Json
Eu abordo mais maneiras de salvar objetos em um arquivo no meu artigo sobre As muitas maneiras de ler e gravar em arquivos.
Trabalhar com propriedades
Adicionando propriedades
Você ainda pode adicionar novas propriedades ao seu PSCustomObject
com Add-Member
o .
$myObject | Add-Member -MemberType NoteProperty -Name 'ID' -Value 'KevinMarquette'
$myObject.ID
Remover propriedades
Você também pode remover propriedades de um objeto.
$myObject.psobject.properties.remove('ID')
O .psobject
é um membro intrínseco que lhe dá acesso aos metadados do objeto de base. Para obter mais informações sobre membros intrínsecos, consulte about_Intrinsic_Members.
Enumerando nomes de propriedade
Às vezes, você precisa de uma lista de todos os nomes de propriedade em um objeto.
$myObject | Get-Member -MemberType NoteProperty | Select -ExpandProperty Name
Podemos obter esta mesma lista fora da psobject
propriedade também.
$myobject.psobject.properties.name
Nota
Get-Member
Retorna as propriedades em ordem alfabética. Usar o operador member-access para enumerar os nomes de propriedade retorna as propriedades na ordem em que foram definidas no objeto.
Acesso dinâmico às propriedades
Já mencionei que você pode acessar valores de propriedade diretamente.
$myObject.Name
Você pode usar uma cadeia de caracteres para o nome da propriedade e ela ainda funcionará.
$myObject.'Name'
Podemos dar mais um passo e usar uma variável para o nome do imóvel.
$property = 'Name'
$myObject.$property
Eu sei que parece estranho, mas funciona.
Converter PSCustomObject em uma hashtable
Para continuar a partir da última seção, você pode percorrer dinamicamente as propriedades e criar uma hashtable a partir delas.
$hashtable = @{}
foreach( $property in $myobject.psobject.properties.name )
{
$hashtable[$property] = $myObject.$property
}
Teste de propriedades
Se você precisa saber se existe um imóvel, basta verificar se esse imóvel tem um valor.
if( $null -ne $myObject.ID )
Mas se o valor pode ser $null
, você pode verificar se ele existe, verificando o psobject.properties
para ele.
if( $myobject.psobject.properties.match('ID').Count )
Adicionando métodos de objeto
Se você precisar adicionar um método de script a um objeto, poderá fazê-lo com Add-Member
e um ScriptBlock
arquivo . Você tem que usar a this
variável automática referenciar o objeto atual. Aqui está um scriptblock
para transformar um objeto em uma hashtable. (mesmo código do último exemplo)
$ScriptBlock = {
$hashtable = @{}
foreach( $property in $this.psobject.properties.name )
{
$hashtable[$property] = $this.$property
}
return $hashtable
}
Em seguida, adicionamo-lo ao nosso objeto como uma propriedade de script.
$memberParam = @{
MemberType = "ScriptMethod"
InputObject = $myobject
Name = "ToHashtable"
Value = $scriptBlock
}
Add-Member @memberParam
Então podemos chamar a nossa função assim:
$myObject.ToHashtable()
Tipos de objetos vs valores
Objetos e tipos de valor não lidam com atribuições variáveis da mesma maneira. Se você atribuir tipos de valor uns aos outros, somente o valor será copiado para a nova variável.
$first = 1
$second = $first
$second = 2
Neste caso, $first
é 1 e $second
é 2.
As variáveis de objeto contêm uma referência ao objeto real. Quando você atribui um objeto a uma nova variável, eles ainda fazem referência ao mesmo objeto.
$third = [PSCustomObject]@{Key=3}
$fourth = $third
$fourth.Key = 4
Porque $third
e $fourth
referenciar a mesma instância de um objeto, ambos $third.key
e $fourth.Key
são 4.
psobject.copy()
Se precisar de uma cópia verdadeira de um objeto, você pode cloná-lo.
$third = [PSCustomObject]@{Key=3}
$fourth = $third.psobject.copy()
$fourth.Key = 4
O clone cria uma cópia superficial do objeto. Eles têm instâncias diferentes agora e $third.key
é 3 e $fourth.Key
é 4 neste exemplo.
Eu chamo isso de cópia superficial porque se você tiver objetos aninhados (objetos com propriedades contêm outros objetos), somente os valores de nível superior serão copiados. Os objetos filho farão referência uns aos outros.
PSTypeName para tipos de objeto personalizados
Agora que temos um objeto, há mais algumas coisas que podemos fazer com ele que podem não ser tão óbvias. A primeira coisa que temos de fazer é dar-lhe um PSTypeName
. Esta é a forma mais comum de ver as pessoas a fazê-lo:
$myObject.PSObject.TypeNames.Insert(0,"My.Object")
Eu descobri recentemente outra maneira de fazer isso do Redditor u/markekraus
. Ele fala sobre essa abordagem que permite defini-la em linha.
$myObject = [PSCustomObject]@{
PSTypeName = 'My.Object'
Name = 'Kevin'
Language = 'PowerShell'
State = 'Texas'
}
Eu amo como isso se encaixa bem na linguagem. Agora que temos um objeto com um nome de tipo próprio, podemos fazer mais algumas coisas.
Nota
Você também pode criar tipos personalizados do PowerShell usando classes do PowerShell. Para obter mais informações, consulte Visão geral da classe do PowerShell.
Usando DefaultPropertySet (o caminho longo)
O PowerShell decide por padrão quais propriedades exibir. Muitos dos comandos nativos têm um .ps1xml
arquivo de formatação que faz todo o trabalho pesado. A partir deste post da Boe Prox, há outra maneira de fazer isso em nosso objeto personalizado usando apenas o PowerShell. Podemos dar-lhe um MemberSet
para ele usar.
$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
Agora, quando meu objeto cai no shell, ele só mostrará essas propriedades por padrão.
Update-TypeData com DefaultPropertySet
Isso é bom, mas recentemente vi uma maneira melhor usando Update-TypeData para especificar as propriedades padrão.
$TypeData = @{
TypeName = 'My.Object'
DefaultDisplayPropertySet = 'Name','Language'
}
Update-TypeData @TypeData
Isso é simples o suficiente para que eu quase pudesse me lembrar se eu não tivesse este post como uma referência rápida. Agora eu posso facilmente criar objetos com muitas propriedades e ainda dar-lhe uma bela visão limpa ao olhar para ele a partir da concha. Se eu precisar acessar ou ver essas outras propriedades, elas ainda estarão lá.
$myObject | Format-List *
Update-TypeData com ScriptProperty
Outra coisa que eu consegui desse vídeo foi criar propriedades de script para seus objetos. Este seria um bom momento para salientar que isso também funciona para objetos existentes.
$TypeData = @{
TypeName = 'My.Object'
MemberType = 'ScriptProperty'
MemberName = 'UpperCaseName'
Value = {$this.Name.toUpper()}
}
Update-TypeData @TypeData
Você pode fazer isso antes que seu objeto seja criado ou depois e ele ainda funcionará. Isso é o que torna isso diferente de usar Add-Member
com uma propriedade de script. Quando você usa Add-Member
a maneira que mencionei anteriormente, ela só existe nessa instância específica do objeto. Este aplica-se a todos os objetos com este TypeName
.
Parâmetros de função
Agora você pode usar esses tipos personalizados para parâmetros em suas funções e scripts. Você pode fazer com que uma função crie esses objetos personalizados e, em seguida, passá-los para outras funções.
param( [PSTypeName('My.Object')]$Data )
O PowerShell requer que o objeto seja do tipo especificado. Ele lança um erro de validação se o tipo não corresponder automaticamente para salvar a etapa de teste para ele em seu código. Um ótimo exemplo de como o PowerShell faz o que faz melhor.
Função OutputType
Você também pode definir um OutputType
para suas funções avançadas.
function Get-MyObject
{
[OutputType('My.Object')]
[CmdletBinding()]
param
(
...
O valor do atributo OutputType é apenas uma nota de documentação. Ele não é derivado do código da função ou comparado com a saída da função real.
A principal razão pela qual você usaria um tipo de saída é para que as informações meta sobre sua função reflitam suas intenções. Coisas como Get-Command
e Get-Help
que seu ambiente de desenvolvimento pode aproveitar. Se você quiser mais informações, então dê uma olhada na ajuda para isso: about_Functions_OutputTypeAttribute.
Dito isso, se você estiver usando o Pester para testar suas funções por unidade, seria uma boa ideia validar os objetos de saída correspondentes ao seu OutputType. Isso poderia pegar variáveis que simplesmente caem no tubo quando não deveriam.
Considerações finais
O contexto disso era todo sobre [PSCustomObject]
, mas muitas dessas informações se aplicam a objetos em geral.
Eu já vi a maioria desses recursos de passagem antes, mas nunca os vi apresentados como uma coleção de informações sobre PSCustomObject
. Só esta última semana me deparei com outro e fiquei surpreendido por não o ter visto antes. Eu queria reunir todas essas ideias para que você possa ver o quadro geral e estar ciente delas quando tiver a oportunidade de usá-las. Espero que você tenha aprendido algo e possa encontrar uma maneira de trabalhar isso em seus scripts.