I'm looking for a way to dynamically call a method or property of a function. For example say I wanted to call every property of $Error[0]. I could get a list of all the property names with:
$a = ($error[0] | get-member -type property | select Name)
I would then like to do something like this:
foreach($b in $a){
$error[0].&($b.Name)
}
But the call operator (&) doesn't resolve $b.Name like I would expect (it should resolve to 'CatagoryInfo'). Is there anyway to do something like this?
You could use different way to retrieve properties:
$Properties=$Error[0].PSObject.Properties
This will return collection of PSPropertyInfo objects. PSPropertyInfo objects have Value property which you could use to get or set property value:
$Properties|ForEach-Object {'Name={0}; Value={1}'-f$_.Name,$_.Value}
Also note of some possible problems in your way of retrieving properties' values. PowerShell allows you to access IDictionary elements with property syntax. Problem here is that PowerShell prefer to access to a collection element rather than actual property:
$Hashtable=#{}
$Hashtable.Count
# 0
$Hashtable.Add('SomeName',10)
$Hashtable.SomeName
# 10
$Hashtable.Count
# 1
$Hashtable.Add('Count',20)
$Hashtable.Count
# 20
$Hashtable|Select-Object -ExpandProperty Count
# 2
You can use Invoke-Expression:
$a = ($error[0] | get-member -type property | select Name)
foreach($b in $a){
Invoke-Expression "`$error[0].$($b.Name)"
}
Just remove the &
$a = ($error[0] | get-member -type property | select Name)
foreach($b in $a){
$error[0].($b.Name)
}
Related
I have a a similar question like this: Question 44460843
I want to use variables for the fields.
This example works well:
$x = ("Id,ProcessName,CPU").split(",")
Get-Process | ft -a $x
But how do i get it, if i want custom fields like this?
Get-Process | ft -a Id,ProcessName,#{Name = 'CPUx' ; Expression = {$_.CPU}}
I tried this, but it don't works:
$x = ("Id,ProcessName,#{Name = 'CPUx' ; Expression = {$_.CPU}}").split(",")
Get-Process | ft -a $x
Does anybody know how to do this?
I am a lazy guy and want to avoid this copy/paste orgies with long text.
Note:
The answer applies analogously to the Select-Object cmdlet, which is what should be used to extract properties for later programmatic processing.
Format-* cmdlets, such as Format-Table in this case (whose built-in alias is ft), are only intended to produce for-display formatting; see this answer for more information.
Theo has provided the solution in a comment:
$x = 'Id', 'ProcessName', #{ Name = 'CPUx' ; Expression = { $_.CPU } }
Get-Process | Format-Table -AutoSize $x # same as: ft -a $x
That is, the answer to the linked question applies in your case as well, even though it happens to use only literal strings as property names:
Directly construct an array of property names and calculated properties, using ,, the array constructor operator, which allows you to use any mix of literals and variables; e.g.:
$pName = 'processName' # property name
$pCpu = #{ Name = 'CPUx' ; Expression = { $_.CPU } } # calculated property
$x = 'Id', $pName, $pCpu
Do not start with a single string that you split into an array of tokens with .Split(), because it will limit you to property names, given that the tokens are invariably strings.
As an aside:
The array binds positionally to Format-Table's (ft's) -Property parameter, and you can easily discover that as well as the fact that an array of property names / calculated properties is discovered as follows:
PS> Format-Table -?
...
SYNTAX
Format-Table [[-Property] <System.Object[]>] ...
DESCRIPTION
...
The outer [...] tells you that the parameter is optional as a whole.
The [...] around -Property tells you that specifying the parameter name explicitly is optional, i.e. that positional binding is supported.
<System.Object[]> tells you that an array ([]) of System.Object instances is expected as the type of the argument(s).
To get more information about what objects, specifically, may be passed, inspect the parameter individually:
PS> Get-Help Format-Table -Parameter Property
-Property <System.Object[]>
Specifies the object properties that appear in the display and the order
in which they appear. Type one or more property names, separated
by commas, or use a hash table to display a calculated property.
Wildcards are permitted.
...
I want to collect data remotely and adapt the table headers later with the help of an xml-file. This should happen in a loop, looking like that:
foreach($tableheader in $table) {
$table.$tableheader = $xmlFile.$tableheader
}
Amongst others I tried the following:
$x = 0
$sitesonfig = Get-ConfigSite -AdminAddress localhost
foreach($Prop in ($siteconfig |get-member -MemberType Property | select -Property name))
{
$x += 1;
$siteconfig = $siteconfig | Select-Object * | format-table #{l="Smile$x";e={$_.$Prop}}
}
Yes, I know this looks silly, but I've got really no idea, how to change the headers one by one without listing each time all the other headers, too.
One possibility is to use a loop to create the header map that you pass into Format-Table.
Here is your second example modified to demonstrate this concept. You should be able to adapt this to grab the header info from your XML file.
$x = 0
$siteconfig = Get-ConfigSite -AdminAddress localhost
$headerMap = #()
foreach($Prop in ($siteconfig |get-member -MemberType Property | select -ExpandProperty name))
{
$x += 1;
$headerMap += #{
l="Smile$x";
e={ $_.$Prop }.GetNewClosure()
}
}
$siteconfig | Format-Table $headerMap
Important Points
Select -Property name needed to be changed to Select -ExpandProperty name. The reason for this is that Select-Object in PowerShell will return an object filtered down to the selected member but you need a string for grabbing the property value by name. The -ExpandProperty parameter will expand this to be the string value instead.
The expression block needs GetNewClosure() called on it to capture the value of $Prop at the time of script block creation versus at the time of calling. This will probably be a little confusing if you are new to the concept of closures and PowerShell's scoping rules. Without this, due to PowerShell's scoping rules, $Prop will evaluate to the value of $Prop at the time it is used by Format-Table. By calling GetNewClosure(), the value of $Prop is captured when GetNewClosure() is called which is what we want in this case.
I'd like to select items from the pipeline using "select" but it returns raw data like: #{Name=MyMachine}
This will not be helpful if, say I want to reboot #{Name=MyMachine} because there is not machine named #{Name=MyMachine}
There's a "MyMachine" but the script does not return just the name.
How do you strip out the "#{Name="}" when selecting individual objects?
Use the -ExpandProperty parameter of Select-Object:
$someobjects | Select-Object -ExpandProperty Name
Another option is echoing the property in a loop:
$something | % { $_.Name }
How can I find what properties object $a has in the following?
$a = 1
$a.length
$a | Get-Member
Get-Member does not seem to produce any properties for object $a? Is length a property of object $a?
$a is an Integer, it doesn't have a length property. Using Get-member is the right way to find object properties.
You can also pipe a sample object to Select-Object to see all properties and their values.
get-process | select -first 1 -prop *
I have the following output from a PowerShell command and want to update the value for EmployeeID
I can filter the output with $test.identifiers | where {$_.name -like "EmployeeID" }
But if I try to update the value with
$test.identifiers | where {$_.name -like "EmployeeID" } | foreach {$_.values.value = "098324"}
I get an error
How can I update this nested value?
$_.values contains an array (or collection) objects, which explains why you can get (read) the .value property, but not set (write) it (see below).
If you expect the array to have just one element, simply use [0] to access that element directly:
$test.identifiers | where {$_.name -like "EmployeeID" } | foreach {
$_.values[0].value = '098324'
}
If there are multiple elements, use
$_.values | foreach { $_.value = '098324' } to assign to them all, or, alternatively in PSv4+,
$_.values.ForEach('value', '098324')
In PSv3+ a feature called member-access enumeration allows you to access a property on a collection and have the property values from the individual elements returned as an array.
However, that only works for getting properties, not for setting them.
When you try to set, only the collection's own properties are considered, which explains the error you saw - an array itself has no .value property.
While this asymmetry is by design, to avoid potentially unwanted bulk modification, the error message could certainly be more helpful.
Simple reproduction of the problem:
Create an object with property one containing a single-element array with another object, with property two:
$obj = [pscustomobject] #{ one = , [pscustomobject] #{ two = 2 } }
The default output looks as follows:
PS> $obj
one
---
{#{two=2}}
The outer {...} indicate an array, as in your case, and what's inside is a
hashtable-like notation that PowerShell uses to represent custom objects.
Getting the nested-inside-an-array object's two property works as intended:
PS> $obj.two
2
Trying to set it fails:
PS> $obj.two = 2.1
The property 'two' cannot be found on this object. Verify that the property exists and can be set.
To set, use .ForEach(), for instance:
PS> $obj.ForEach('two', 2.1); $obj
one
---
{#{two=2.1}}
Have you tried it this way with the full object path:
$test.identifiers | where {$_.name -like "EmployeeID" } | foreach {$_.identifiers.values.value = "098324"}