powershell - How refer custom expression property on single one select-object - powershell

can you help me with this simple example script?
How do I refer to "custom expression property" on single one "select-object"?
I would like: single "select-object"
Get-Process | Select-Object Id, 
    #{name="MyProp"; expression={$_.id}},
    #{name="MyPropRef"; expression={$_.MyProp}}
...but third property "MyPropRef" is not displayed!
Id MyProp MyPropRef
-- ------ ---------
3780 3780
While with double select-object piped, "MyPropRef" is displayed
Get-Process | Select-Object Id, 
    #{name="MyProp"; expression={$_.id}} | Select-Object *,
    #{name="MyPropRef"; expression={$_.MyProp}}
Id MyProp MyPropRef
-- ------ ---------
3780 3780 3780
Thanks

Calculated properties only operate on the input objects' original properties, they cannot refer to each other.
If you need to add additional properties based on previously calculated properties, you indeed need another (expensive) Select-Object call.
Your command:
Get-Process | Select-Object Id,
#{name="MyProp"; expression={$_.id}},
#{name="MyPropRef"; expression={$_.MyProp}}
creates a .MyPropRef property, but its value is $null, because the System.Diagnostics.Process instances that Get-Process outputs themselves do not have a .MyPropRef property.
If you want to make do with a single Select-Object call without repetition, you can define the script blocks used in the calculated properties in variables beforehand and reuse them across calculated property definitions; e.g.:
$myPropExpr = { $_.id }
Get-Process | Select-Object Id,
#{ name="MyProp"; expression=$myPropExpr },
#{ name="MyPropRef"; expression={ "!" + (& $myPropExpr) }}
This would yield something like:
Id MyProp MyPropRef
-- ------ ---------
3780 3780 !3780
...

Related

How does pipeline affect Select-Object?

I'm trying to make a table in PowerShell with custom headers, I am able to do this with
"var" | Select #{n="First";e={"1"}}, #{n="Second";e={"2"}},#{n="Third";e={"3"}}
First Second Third
----- ------ -----
1 2 3
However, without the initial object, there is no output
Select #{n="First";e={"1"}}, #{n="Second";e={"2"}},#{n="Third";e={"3"}}
I can't tell the difference between these other than one is after a pipeline while the other isn't. Why won't this work?
Why doesn't this work?
The cmdlet Select-Object (alias Select) has one required parameter which is -inputObject Which also happens to be the object that gets passed through the pipeline.
Select-Object -InputObject "Example" -Property #{n="First";e={"1"}}, #{n="Second";e={"2"}},#{n="Third";e={"3"}}
Will have the output
First Second Third
----- ------ -----
1 2 3
While without -InputObject, it will not have an output because there is no input (Thanks #mklement0).
Select-Object "Example" -Property #{n="First";e={"1"}}, #{n="Second";e={"2"}},#{n="Third";e={"3"}}
# No Output
and with the pipeline, it will
"Example" | Select-Object "Example" -Property #{n="First";e={"1"}}, #{n="Second";e={"2"}},#{n="Third";e={"3"}}
First Second Third
----- ------ -----
1 2 3
The -inputObject Parameter will usually contain the table you would like to select columns from or other things (if you are not using expressions).
Although the answer from #Neko answers the exact question, I think it is important to mention that the Select-Object cmdlet is not mentioned to construct new custom objects:
Quote from the Get-Help Select-Object -Online:
The Select-Object cmdlet selects specified properties of an object
or set of objects.
In other words; the object needs to exist in order to select its properties.
For what you are doing with
"var" | Select #{n="First";e={"1"}}, #{n="Second";e={"2"}},#{n="Third";e={"3"}}`
you're creating a new pscustomobject by removing all the default properties from the "Var" (string) object and adding new properties with a hard-coded (static) expression which is a long-winded syntax and quiet expensive for building a new object.
To construct a new pscustomobject ("table"), you can simply use the constructor syntax:
[pscustomobject]#{First = '1'; Second = '2'; Third = '3'}
For legacy (prior PSv3) POwerShell versions:
New-Object PSObject -Property #{First = '1'; Second = '2'; Third = '3'}

Using a function in a calculated field in Powershell?

So I have this:
get-ADUser -SearchRoot 'boogers.com/Locations/Kleenex' |
Where-Object { $_.TITLE -ne 'Snot' } |
Select LastName,FirstName,Description,SamAccountName,Department,TITLE, #{Name='bs_value';Expression=#{Name='TITLE'; Expression={(Get-Culture).TextInfo.ToTitleCase($_.Title.ToLower())}}
And it works great, until, oh I dunno, I want another calculated field...then it becomes JUST TOO LONG!
So I write a function:
function tc($name, $str) {
return #{Name=$name; Expression={(Get-Culture).TextInfo.ToTitleCase($str.ToLower())}}
}
Seems reasonable enough, no?
But if I run the function:
$bob = tc 'Title' 'bob'
I get:
Name Value
---- -----
Expression (Get-Culture).TextInfo.ToTitleCase($str.ToLower())
Name Title
Instead of...
Name Value
---- -----
Expression "Bob"
Name Title
I just want the code to be shorter! I'm using a computer!
since you didn't show how you were calling the modified function i mentioned in my comment, i presume you were not using it correctly. [grin] your function is ... rather peculiar, so i rewrote it to work in a more normal manner.
function ConvertTo-TitleCase ([string]$InString)
{
(Get-Culture).TextInfo.ToTitleCase($InString.ToLower())
}
Get-LocalUser |
Where-Object {
$_.Name -notmatch "Admin|Guest|Home|$env:USERNAME"
} |
Select-Object -Property Name, FullName, Enabled,
#{Name='bs_value';Expression={ConvertTo-TitleCase -InString $_.Description}}
output from my local use list ...
Name FullName Enabled bs_value
---- -------- ------- --------
22 2 Digit 2 True The Digit 2 Twice
TooToo Too Also Too True The Word For Also, Repeated Twice
ToTo To Thataway To True The Destination Designator, Two Times.
Tutu Tutu Dress Tutu True The Ballet Apparel For Ladies.
TwoTwo Two Number Two True Repeating The Name Of The Number After One.

save the name of VM to array using powershell

This command saves list of VM to array.
[string]$arrayVM = Get-VM | select Name
When it refers to the first element of the array: $arrayVM[0]
I get the name of VM, but this value look like: #{name=MACHNINE_1_NAME}
I expected only the same name: MACHNINE_1_NAME
How can I do this?
You need to add the -ExpandProperty parameter to your Select-Object command. What this does is expands the value of the specified property and passes that down the pipeline instead of restricting the properties of the current object to be passed down the pipeline. Right now you are passing a Microsoft.HyperV.PowerShell.VirtualMachine object with only the Name property intact. This will instead pass a string with the value of the Name property.
$arrayVM = Get-VM | select -expand Name
[string]$ArrayVM = (Get-VM | Select-Object -Property Name).Name
[string]$arrayVM = Get-VM | select Name
$arrayVM[0].Name

Extracting text from output

I am creating my first application with VS and PowerShell and need to get the name of a service from my listview. The output of the selection looks like this:
ComputerName Status Name DisplayName
------------ ------ ---- -----------
PC Running Appinfo Application Information
What I want to do is get the value Appinfo from the Name column and assign it to a variable. I've had no luck with regex, but then again I am a beginner so I could be doing something wrong. Is there an easy way to do this?
The output you're currently getting is a formatted (tabular) view on selected properties of an object (namely the properties ComputerName, Status, Name, and DisplayName). You can get the value of a particular property by expanding it via the Select-Object cmdlet:
$name = ... | Select-Object -Expand Name
You could also store the object in a variable and access the property via dot-notation:
$obj = ...
$name = $obj.Name
Beware that if a cmdlet outputs multiple objects the variable will contain an array:
PS C:\> $services = Get-Service
PS C:\> $services.GetType().FullName
System.Object[]
PS C:\> $services.Count
136
You can access properties of a member object by index:
$services[42].Name
or by selecting a specific object via its properties:
$services | Where-Object { $_.DisplayName -eq 'Application Information' } |
Select-Object -Expand Name
Since PowerShell v3 you can also use the dot-property notation to get the value of a particular property of all array members:
$services.Name
Prior to PowerShell v3 this would have thrown an error, because the array object doesn't have a property Name.

Powershell - When an object is placed on the pipeline, can it be used again later?

I have a collection of objects which I am trying to return multiple sets of data from. Each time I want to select one of the properties of the object and sort it uniquely.
The problem I'm getting is that I only get results the first time I use the collection. This leads me to wonder whether the objects are being "consumed" when I put them on the pipeline.
To illustrate with an example:
$results = getMyCollectionOfObjects
$results | select-object property1 | sort-object -unique property1
$results | select-object property2 | sort-object -unique property2
$results | select-object property3 | sort-object -unique property3
As far as I can tell this should result in 3 distinct lists of all the possible values for each of the three properties.
However for me it's like the $results value is being "used up" the first time it is selected from.
Is this the case and what should I do instead?
Hope this is clear.
That's how it rolls... you have to explicitly pipe them to Out-Default to get rid of that odd behavior. Otherwise it will try to display property1 for 2nd and 3rd set too. You removed it from $results, so it comes back blank.
HTH
Bartek
So this had me tripped up and scratching my head or a minute as well. The answer it turns out is really quite simple: the Select-Object cmdlet returns an object of type Selected.System.Management.Automation.PSCustomObject. That is then passed down the pipeline to following two selects, but since there are no longer any matching properties (they were discarded from the first select) - nothing is output. Consider the following example:
# declare an array and populate it
$results = #()
$results = $results + (New-Object PSobject -Property #{
P1 = "One"
P2 = "Two"
P3 = "Three"
})
$results = $results + (New-Object PSobject -Property #{
P1 = "Uno"
P2 = "Dos"
P3 = "Tres"
})
$results | select P1
$results | select P2
$results | select P3
As you described, I was only getting output from the first select. I them took the suggestion from BartekB to put | Out-Default at the end of each line and it started working. I investigated further by replacing that with | Get-Member to view the object the was being placed on the pipeline:
$results | select -Property P1 | get-member
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()
P1 NoteProperty System.String P1=One
In a nutshell, the Out-Default is required to actually force it to display on the console instead of passing it along to the next statement. It would seem this behavior is implied upon completing a statement in the interactive shell, but behaves a bit differently when fully scripted.