PowerShell custom output #{n=;e=} - powershell

I'm not very clear on how the #{n=;e=} construct works in PowerShell.
Does this type of thing have a name that I can find examples from?
For example, I find examples like this that works great:
gwmi win32_logicaldisk | Format-Table DeviceId, VolumeName, #{n="Size(GB)";e={[math]::Round($_.Size/1GB,2)}},#{n="Free(GB)";e={[math]::Round($_.FreeSpace/1GB,2)}}
When I try to do something like that, I can never get it to work. This works fine:
Get-Command -Module Microsoft.Powershell.Utility | Where CommandType -eq Function | Select Name,Version,CommandType
So I thought I would try and add the definition of that function to a new column using cat function:\$_.Name
Get-Command -Module Microsoft.Powershell.Utility | Where CommandType -eq Function | Select Name,Version,CommandType,#{n="Contents"; e={cat function:\$_.Name}}
But I just get an empty Contents column :(
Can someone give me some pointers on how the #{n=;e=} construct works?
Also, what do the n and e stand for?

#{n='';e={}} syntax is called a calculated property. n stands for Name and e stands for expression. You can even specify Name instead of n and Expression instead of e.
Calculated properties allow you to create new properties or change existing ones. This is done by passing a special hashtable to the Property parameter rather than a static property name. This is a useful feature where you create new properties using custom expression in a script block and use existing properties.
Not for only Select-Object this work for also Format-Table, Format-List cmdlets. These don't work outside this cmdlets.
Calculated properties are a quick way to manipulate command output to return just about anything you like. These save your time and reduce code length.
Sidenote: The last code in your question dosent work because you need to join two path using Join-Path. Calculated properties are innocent here. Even you can join path like this: cat "Function:\$($_.Name)" as #MathiasR.Jessen pointed.

#{} is a hashtable. {} is a scriptblock inside the hashtable set equal to e. The hashtable is used in a custom way for select-object.
$scriptblock = { $_ }
$hashtable = #{ name = 'number'
expression = $scriptblock
}
1..3 | select-object -property $hashtable
number
------
1
2
3

Related

How to call multiple properties in powershell

$loadbalancer = Get-AzureRmLoadBalancer | select Name,ResourceGroupName
$loadbalancer has two value ILB name and its resource group.
I want call both value in single PS command, Like when I call ILB name its respective resource group should be called.
The problem isn't entirely clear, can you please give an example of the function being used in the next step. I used "get-command" for the examples rather than get-azurermloadbalancer to avoid being package dependent.
To get the propety from $loadbalancer you use a dot and the name.
To get the current row from pipeline you can use $_
$loadbalancer = Get-Command get-* | Select-Object -Property CommandType,Name
#get a particular property
$loadbalancer.CommandType
#Use both properties in one function
$loadbalancer | Where-Object{($_.CommandType -eq "function") -and ($_.Name -match "Token")}

Is it possible to use a cmdlet as an input parameter for a dynamic PowerShell script?

Here's a test script I'm trying to use, and I'm calling it from a separate process and attempting to pass parameters to it. The idea is that I have a user interface that allows a user to select a CmdLet and then populate another dropdown with the properties/methods of that CmdLet.
My problem seems to be that the script is rendering the input parameter as a string, and is thusly creating a text file with the methods and properties of any arbitrary string to which you've applied a "Get-Member" to, such as "Clone", or "CompareTo". The only property as such is "Length".
Is there any way to have that input parameter be brought over as a usable CmdLet instead of a string? Perhaps I'm missing something, or perhaps what I'm attempting to do isn't possible.
param([string]$inputCmdLet = "Get-NetAdapter");
$wrkgDir = "D:\Distribution\Operational";
# Get Properties and Methods for CmdLet Input Parameter
$propertyNames = $inputCmdLet | Get-Member -MemberType Property;
$methodNames = $inputCmdLet | Get-Member -MemberType Method;
# Sort Arrays
$propertyNames = $propertyNames | Sort-Object Name;
$methodNames = $methodNames | Sort-Object Name;
# Output Results to Text Files
$propertyNames.Name | Out-File $wrkgDir\$inputCmdLet.Properties.txt;
$methodNames.Name | Out-File $wrkgDir\$inputCmdLet.Methods.txt;
EDIT FOR MORE INFO:
The output I'm hoping for, in the example of Get-NetAdapter, is the list of properties in one output file and methods in the other. What I'm getting now is this:
Left list is expected (partial) result, right list is actual result.
I'm uncertain how to achieve the result list on the left (in the image) programmatically. I'm able to get the proper output by typing it out statically:
$mbrNameStatic = Get-NetAdapter | Get-Member;
$mbrNameStatic.Name | Out-File $wrkgDir\$inputCmdLet.Strings.txt;
But when i use the input parameter, it merges the value in as a string, so it seems the actual runtime code looks more like this:
$propertyNames = "Get-NetAdapter" | Get-Member -MemberType Property;
So the addition of the quotes renders the cmdlet as a string (makes sense i suppose, since my input parameter is a string), which returns the properties and methods of a string instead of the cmdlet. Is there any way to have the cmdlet render out without the quotes?
Please do let me know if I'm not making sense with this, either with my description, or with the idea altogether.
Thanks!
In order to execute a command whose name (only) is stored in a variable or whose name is specified in single or double quotes, you must use &, the call operator.
# WRONG: The token is interpreted as an *expression* that outputs a *string*
"Get-NetAdapter" # outputs the [string] literal
# WRONG: ditto, via a variable
$name = "Get-NetAdapter"
$name # outputs the contents of the [string] variable
# OK: Use of & tells Powershell to interpret the next token as a *command* to *invoke*.
& "Get-NetAdapter"
& $name
As for your general approach:
Note that not all cmdlets produce output when invoked without parameters, so your current code (even with &) won't work with all cmdlets.
Conversely, those cmdlets that do produce output when given no parameters may produce a lot of them, which is unnecessary, so consider something like & $inputCmdlet | Select-Object -First 1.
Generically, you can use something like (Get-Command Get-NetAdapter).OutputType to obtain a cmdlet's output type(s), but note that:
Declaring output types is optional, so not all cmdlets may return a value.
If you start with a type rater than an instance of that type, you cannot use Get-Member to discover the instance members (you can only obtain the static members via -Static).

Does Get-WebApplication returns invalid objects?

Why do I don't get valid names, if I use Get-WebApplication in PowerShell like this:
Get-WebApplication | ForEach-Object { write-host $_.Name }
In the result set, the name properties are empty. Why?
Instead Get-WebApplication does return a structure like:
PS> Get-WebApplication
Name Application pool Protocols Physical Path
---- ---------------- --------- -------------
test default http C:\....
Assuming Get-WebApplication is https://technet.microsoft.com/en-us/library/ee790554.aspx.
Name column in the output from Get-WebApplication seems to behave a little like a calculated property. I was unable to find out exactly what the formula is behind it but by running
Get-WebApplication | select -ExpandProperty Path | select #{ n="Name";e={ $_.TrimStart('/') } }
you will get a similar looking result as to what you appear to be trying to extract.
This command selects and simplifies the Path property (essentially turns it into a string), then creates from it a calculated property that has the correct title and value.

format list of synopsis for a custom list of functions

I have a powershell script containing several functions representing tasks. I would like to format a list of those functions with for each function its name and the synopsis provided in the documentation.
For one function Foo:
Get-Help Foo | Format-Table -Property Name, Synopsis
I don't know how to make it work with multiple functions. I am having troubles with the power syntax. I don't know how to declare a list of function because Foo, Bar is a syntax error. I also tried to list the names of the function as strings and convert those to the corresponding objects but I failed to do so.
How to print the name and synopsis for a custom list of functions?
As you figured out yourself, the Get-Help (alias help) cmdlet doesnt expose a parameter set with multiple -name parameters. However, You can define an array of your functions, iterate over it and call the Get-Help for each of them. Example:
#('Get-Content', 'Get-ChildItem') | foreach { help $_ } | Format-Table -Property Name, Synopsis
Output:
Name Synopsis
---- --------
Get-Content Gets the content of the item at the specified location.
Get-ChildItem Gets the items and child items in one or more specified locations.

How do I select certain hashtable values but change others in-line in a powershell script

Using piping or other sort of inline notation, I'd like to "select" certain values in a hash table straight through to the result, while changing or setting others.
Hash Table
I'm starting with a hash table (which, frankly is being used like an object), like the following:
>$hash
Name Value
---- -----
System server-1
Job Microsoft.PowerShell.Commands.PSWmiJob
Result
Is there a way to use the Select-Object cmdlet (or anything like it) to set the value of certain properties and pass through others? I'd like to end up with this:
>$hash
Name Value
---- -----
System server-1
Job Microsoft.PowerShell.Commands.PSWmiJob
Result Good
If I didn't need/want it to be inline, I simply set the value of Result:
$hash.Result = "good"
For an inline solution, I could always use Foreach-Object like this:
$hash | Foreach-Object {#{System=$_.System;Job=$_.Job;Result="Good"}}
or just directly refer to $hash in a sub-expression:
${#{System=$hash.System;Job=$hash.Job;Result="Good"}}
However, it seems nonsensical to use Foreach-Object when I'm only processing the one hash table, and quite verbose to mention properties twice that I just want to pass through anyways, and then can be unwieldy if my $hash variable is $aSignificantlyLongerHashTableVariableName.
If I was using a Powershell Object...
If I were using an object like this:
>$obj
System Job Result
------ --- ---
server-1 Microsoft.PowerShell.Commands.PSWmiJob
I would use the select-object Cmdlet like this:
>$obj | Select-Object System, Job, #{Name="Result";Expression="Good"}
which would result in
>$obj
System Job Result
------ --- ---
server-1 Microsoft.PowerShell.Commands.PSWmiJob Good
Hashtable and PSObject have some similar aspects (something that contains other things in a named fashion), but AFAIK, they're incompatible - Is there an alternative closer to Select-Object for what I'm trying to do here?
Being strict about having this as a one-liner that has no setup code beforehand and doesn't modify the source hash, something like this could do the job:
($hash + #{ 'Result' = 'Good' }).GetEnumerator() | ? {$_.Key -in 'Result','System','Job'}
If you're going to be adding something Result = Good regularly, it may be handy to have your little extension bits saved:
$good = #{ 'Result' = 'Good' }
And then your one-liner becomes:
($hash + $good).GetEnumerator() | ? {$_.Key -in 'Result','System','Job'}
Edit: As you noted in the comments below, that one-liner will die if Result exists in the hash. This version is a little safer/more explicit, and more directly maps to the spirit of what you described with Select-Object on PSObjects:
(($hash).GetEnumerator() | ? {$_.Key -in 'System','Job'}) + #{'Result' = 'Good'}
Still not the prettiest thing in the world, but it does the job.
To parse single valued object use this:
$hash = #{}
$hash.Add("System",$obj.System)
$hash.Add("Job"=$obj.job)
$hash.Add("Result"="Good")
If you need more objects - iterate them foreach($o in $obj){$hash.Add()}, but announce the $hash before the cycle.
When you use Select-Object and add a column (property) that didn't exist you're essentially creating a new [PSObject] that wraps the original. You can do this explicitly and use a [hashtable] to initiate its properties, in two ways:
$obj = [PSCustomObject]$hash
or
$obj = New-Object PSObject -Property $hash
From there, you could use Select-Object as usual. You could also manipulate the hash first to have the properties you want and then convert to an object and skip Select-Object.
If you just need to add or update a key in your hash table:
$hash.Result += 'Good'
If the Result key exists, it's value will be set to 'Good'. If it doesn't exist, it will be added with that value.