Trying to add a new member to an array of powershell objects, can't get the expression to evaluate. Here's some example code:
$testData =
@([pscustomobject]@{Name="Cat";Legs=4},
[pscustomobject]@{Name="Parrot";Legs=2},
[pscustomobject]@{Name="Snake";Legs=0})
# this works
$testData | Select-Object Name, Legs, @{N='CopyName';E={$_.Name}}
# why doesnt this work?
$testData | Add-Member -NotePropertyName "CopyName" -NotePropertyValue $_.Name
$testData
(Using Powershell 7)
This works, because you're using a calculated property to define the
CopyNameproperty, and the script block ({ ... }) in yourE(Expression) entry allows you to refer to the.Nameproperty of the input object at hand via the automatic$_variable.Note:
Only a select few cmdlets implement support for calculated properties, notably
Select-Object,Sort-Object,Group-Object,Compare-Objectand theFormat-*cmdlets, via a specific parameter sensibly named-Property(its type is either[object[]]or, in the case ofMeasure-Object, which only supports calculated properties in PowerShell (Core) 7+,[pspropertyexpression[]], and these cmdlets internally use reflection to determine whether a given value is a property name (string) or a calculated property (hashtable / script block)).By contrast, the delay-bind script-block feature (see below) is part of the infrastructure (the parameter binder), and potentially works with any parameter, provided it is (a) declared as pipeline-binding and (b) neither
[object]nor[scriptblock]-typed - again, see below.As an aside: You don't have to explicitly enumerate the existing properties (
Name, Legs): you can refer to them abstractly as*, given that wildcard expressions as property names are supported:$testData | Select-Object *, @{N='CopyName';E={$_.Name}}This doesn't work, because using
$_to refer to the current pipeline input object only works inside a script block.However,
-NotePropertyValue { $_.Name }does not work either:Because the parameter type of
-NotePropertyValueis[object](System.Object),{ $_.Name }would become the property value as-is, as a script block.There is another mechanism (in addition to calculated properties) that allows determining parameter values dynamically: delay-bind script blocks.
However, there are two prerequisites, neither of which is met by
Add-Member's-NotePropertyValueparameter:The targeted parameter must be declared as pipeline-binding (accepting input from the pipeline).
The targeted parameter's type must not be
[object]or[scriptblock]-typed, because you wouldn't be able to distinguish between a value that is meant to be used as-is, as a script block vs. an aux. script block whose purpose is to calculate the actual value.In the case of
Add-Member, the fact that-NotePropertyValueis[object]-typed (to support any value) alone precludes support for this feature.Given the above, you must call
Add-Memberon the objects in$testDataindividually, such as viaForEach-Object: