Tenho um fluxo de trabalho que é bem exigente com os PSCustomObjects que recebe. Costumo usar Select-Object
para obter as propriedades dos objetos que quero manter e, às vezes, preciso converter alguns dos valores para formatos mais utilizáveis. A maneira mais fácil de fazer isso é usar a @{Name='Name'; Expression = {'Expression'}}
técnica.
MAS, essa técnica bagunça o PSCustomObject de uma forma que bloqueia meu fluxo de trabalho futuro.
O problema pode ser reproduzido assim:
$object = [pscustomobject]@{
Text = "This is a string"
}
Na saída abaixo, a definição para string é 'string Text=This is a string'
$object | Select-Object * | Get-Member
<# Outputs
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Text NoteProperty string Text=This is a string
#>
Ao adicionar uma nova NoteProperty com a técnica Select-Object, a definição é 'System.String Text2=This is a string'
É isso que faz meu próximo cmdlet ser executado.
$WhatHappensToText = $object | Select-Object @{Name='Text'; Expression={$_.Text}}
$WhatHappensToText | Get-Member
<# Outputs
TypeName: Selected.System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Text NoteProperty System.String Text=This is a string
#>
Ao remover o excedente como abaixo, a definição retorna para 'string Text2=This is a string'
Exportar para Clixml e reimportar faz o mesmo
$WhatHappensToText | ConvertTo-Json | ConvertFrom-Json | Get-Member
<# Outputs
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Text NoteProperty string Text=This is a string
#>
Se eu adicionar a nova NoteProperty assim, a definição será 'string Text2=This is a string' como eu gosto
$object2 = $object | Select-Object *
$object2 | foreach-object {$_ | Add-Member -MemberType NoteProperty -Name Text2 -Value $_.Text}
$object2 | Get-Member
<# Outputs
TypeName: Selected.System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Text NoteProperty string Text=This is a string
Text2 NoteProperty string Text2=This is a string
#>
Tenho as seguintes perguntas:
Por que a @{Name='Name'; Expression = {'Expression'}}
técnica adiciona System.String à Definição e não string como no cenário Add-Member?
Existe uma maneira de fazer a @{Name='Name'; Expression = {'Expression'}}
técnica adicionar uma string e não um System.String?
Este é um efeito colateral do fato lamentável de que a técnica de propriedade calculada cria um
[psobject]
wrapper em torno do valor da propriedade especificado.[psobject]
destina-se a ser um tipo de auxiliar transparente usado nos bastidores e, embora esses wrappers sejam normalmente invisíveis, eles podem resultar em comportamentos diferentes , como no caso em questão.Esse comportamento problemático, juntamente com uma lista de cenários em que o comportamento muda, é discutido no problema #5579 do GitHub .
Isso requer evitar o
[psobject]
wrapper , o que não é possível com a técnica de propriedade calculada.Suas opções são:
Ou: Use
Add-Member
, como na sua pergunta; se você adicionar o-PassThru
switch, o objeto decorado será passado, permitindo que você use a chamada como parte de um pipeline:Add-Member
em um pipeline não permite que você determine o valor da propriedade dinamicamente , com base em cada objeto de entrada. Embora você possa remediar isso encapsulando a chamada em umaForEach-Object
chamada, a técnica abaixo é mais eficiente.Ou: Use a propriedade intrínseca
psobject
para adicionar uma propriedade a um objeto existente:ForEach-Object
de qualquer forma, o valor da propriedade pode ser derivado dinamicamente do objeto de entrada do pipeline atual, por meio da variável automática$_
, conforme mostrado.