PowerShell : How can I set a variable from a single column in multi-column output? - powershell

I am using Get-PhysicalDisk | Format-Table DeviceID, UniqueID to get a listing of drive number and serial number of all drives on a Windows 2016 Server. I want to search for one serial number and capture only the drive number as a variable. I'm used to awk in UNIX and I'm totally stumped on how to achieve this in PowerShell.
Get-PhysicalDisk | Format-Table DeviceID, UniqueID
DeviceID UniqueID
-------- --------
5 624A937024897B4FF488CBF800027A4B
8 624A937024897B4FF488CBF800028A4D
7 624A937024897B4FF488CBF800027A59
0 {c4d394f5-509e-11e9-a834-806e6f6e6963}
1 {c4d394f6-509e-11e9-a834-806e6f6e6963}
2 {c4d394f7-509e-11e9-a834-806e6f6e6963}
3 {c4d394f8-509e-11e9-a834-806e6f6e6963}
4 {c4d394f9-509e-11e9-a834-806e6f6e6963}
6 624A937024897B4FF488CBF800027A56
I want to expand this command to find SerialNumber 624A937024897B4FF488CBF800027A56 then set a variable called $DriveNumber to the value of 6 as shown in the output.
I then plan to use this variable in Set-Disk to take the drive offline/online as a perform a volume overwrite. I don't want to hard code the drive number because upon reboot, the drive number could change.
NOTE I was using Get-Disk and piping the appropriate output to Set-Disk to perform my drive off/online. But, I have a mysterious issue of the virtual drives not displaying with Get-Disk, therefore I'm trying to find a workaround with Get-PhysicalDisk Thanks!

$driveNumber = (
Get-PhysicalDisk | Where-Object UniqueId -eq '{624A937024897B4FF488CBF800027A56}'
).DeviceId
Note the need to enclose the GUID string in {...}.
As all PowerShell cmdlets do, Get-PhysicalDisk outputs objects whose properties you can query.
Cmdlet Where-Object acts as a filter on the objects it receives from the pipeline and compares the value of property UniqueId to the specified literal GUID (string), which, by definition, matches (at most) one object.
(...).DeviceId returns the value of the target objects' DeviceId property and assigns it to variable $driveNumber.
A note re use of Format-* cmdlets such as Format-Table:
Only ever use Format-* cmdlets for display formatting.
If the intent is further programmatic processing:
either: simply access the input objects' intrinsic properties (whose availability is independent of whether they display by default or via a Format-* cmdlet call)
or: if you need to create simplified or transformed objects with only a subset of the original properties and/or transformed property property values (calculated properties, use Select-Object.

Related

Powershell - Get object properties displayed in console

The point is to populate a variable with a Powershell object's property name displayed in a console.
Meaning, if I run Get-Process, I only want the eight object's properties returned in the console which are 'Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName'.
Get-Member command is not helpful here.
Can anyone help me with that?
Thank you all!
To get the column names - which may or may not be property names - of the table view that is presented for a given .NET type if it has predefined formatting data (that includes a table view) associated with it:
Note:
The following is a proper, but nontrivial and limited solution that derives the column names from the formatting data, using the first table-view definition found. It also has conceptual background information.
See the bottom section for a quick-and-dirty solution for getting the column names only, which uses text parsing to extract the column names directly from a given command's formatted output.
The middle section builds on this first section and extracts a list of property names and calculated properties mirroring the column definitions, which can be used with Select-Object, in order to create custom objects that have properties with the same values that the formatting data produces.
# Determine the .NET type of interest.
$type = (Get-Process)[0].GetType()
# Extract the names of the column headers from the *first* table-view definition.
Get-FormatData $type -PowerShellVersion $PSVersionTable.PSVersion |
ForEach-Object FormatViewDefinition |
Where-Object Control -is [System.Management.Automation.TableControl] |
Select-Object -First 1 |
ForEach-Object {
$i = 0
$rows = $_.Control.Rows
foreach ($colLabel in $_.Control.Headers.Label) {
if ($colLabel) { $colLabel } # Explicit label, with a calculated column value or renamed property
else { $rows.Columns[$i].DisplayEntry.Value } # Property name, with its value as the column value.
++$i
}
}
Caveat: The above limits output to the first table-view definition found - which may or may not apply to a given command. Which definition is chosen by default is potentially governed by criteria associated with the definitions that select based on runtime conditions, including selecting by specific input type, given that a single instance of formatting data can cover multiple types.
Also note that views may involve grouping (as you see in Get-ChildItem's formatted output, for instance), and the grouping criterion isn't covered by the command above.
Note that even for a single type multiple views may be defined, and in order to use a non-default one you must request it explicitly, via Format-Table's -View parameter, assuming you know the name,[1] e.g. Get-Process | Format-Table -View StartTime).
See also:
This answer for how to inspect formatting data in full.
You can alternatively pipe Get-FormatData output to Export-FormatData in order to export formatting data to an XML file, which has the disadvantage of being hard to read, but has the advantage of matching the XML schema used for authoring formatting data - see next point - whereas the in-memory types used to represent formatting data partially use property names that don't match the underlying XML elements.
As for authoring formatting data, which as of PowerShell 7.2.2 requires XML files (*.Format.ps1xml):
See this answer for an example of how to define your own table view.
Formatting File Overview and the Format Schema XML Reference
Note:
Using -PowerShellVersion $PSVersionTable.PSVersion with Get-FormatData is only needed in Windows PowerShell, for certain types, to work around a bug that is no longer present in PowerShell (Core) 7.1+
While column names typically correspond to the property names of the type instances being formatted, that isn't always the case, such as with the [System.Diagnostics.Process] instances output by Get-Process.
A general caveat, as zett42 notes, is that display formatting of types isn't part of the public contract regarding breaking changes, so formatting definitions are allowed to change over time.
If a given type has no predefined formatting data associated with it (in which case Get-FormatData is a quiet no-op):
The names of its (public) instance properties are used as column
names.
You only get a table view by default if there are 4 or fewer properties but you can request it explicitly with Format-Table (With 5 or more properties, Format-List is applied by default).
To get the names of all (public) instance properties of a given object, use the intrinsic .psobject property, which is a rich source of reflection; e.g.:
(Get-Process | Select-Object -First 1).psobject.Properties.Name
To create a list of property names and calculated properties usable with Select-Object that mirror the formatting-data's column definition:
# Determine the .NET type of interest.
$type = (Get-Process)[0].GetType()
# Get an array of property names / calculated properties from the
# formatting data, for later use with Select-Object
$props =
Get-FormatData $type -PowerShellVersion $PSVersionTable.PSVersion |
ForEach-Object FormatViewDefinition |
Where-Object Control -Is [System.Management.Automation.TableControl] |
Select-Object -First 1 |
ForEach-Object {
$i = 0
$rows = $_.Control.Rows
foreach ($colLabel in $_.Control.Headers.Label) {
if ($colLabel) { # Explicit label, with a calculated column value or renamed property
#{
Name = $colLabel
Expression = if ('ScriptBlock' -eq $rows.Columns[$i].DisplayEntry.ValueType) {
[scriptblock]::Create($rows.Columns[$i].DisplayEntry.Value)
} else {
$rows.Columns[$i].DisplayEntry.Value
}
}
}
else { # Property name, with its value as the column value.
$rows.Columns[$i].DisplayEntry.Value
}
++$i
}
}
# Sample call
Get-Process | Select-Object -Property $props | Format-Table | more
The sample call produces similar output to just Get-Process alone, as it uses the column definitions as (calculated) properties - albeit with default values for formatting attributes such as column width and alignment.
Note the explicit use of Format-Table to ensure tabular output; without it - given that the [pscustomobject] instances created by Select-Object have no formatting data associated with them - list formatting (implied Format-List) would result.
As Mathias points out, the calculated properties will be string-typed even for columns based on numeric properties, because their purpose in the formatting data is to created formatted string representations.
Quick-and-dirty solution for getting the column names only:
The following uses Out-String -Stream in conjunction with Select-String to extract the column names from a given command's formatted output, which relies on two assumptions:
The column names have no embedded spaces
The command actually produces table-formatted output by default; however, you can insert a Format-Table call before Out-String, if desired.
Get-Process | Out-String -Stream | Select-String -List '^\s*--+' -Context 1, 0 |
ForEach-Object { -split $_.Context.PreContext[0] }
Output:
NPM(K)
PM(M)
WS(M)
CPU(s)
Id
SI
ProcessName
Note: In Windows PowerShell an additional property shows, as the first one: Handles.
[1] While tab-completion does offer view names, they appear to be out of sync with the actually available ones, as of PowerShell 7.2.2. To see the latter, provoke an error with a dummy name, and the error message will list the available ones; e.g. Get-Process | Format-Table -View NoSuch lists the following available views in the resulting error message: process, Priority, StartTime

Powershell how to use Get-command to display specific columns?

I need to use 'Get-Command" to display three specific columns of information. Name, CommandType, and Module. I am new to PowerShell and have been searching for hours to find an answer but have not been able to find any documentation that explains how to do this specifically.
I need to use 'Get-Command" to display three specific columns of information. Name, CommandType, and Module
Since your intent is to display the columns of interest, in tabular format, you can use Format-Table:
Get-Command | Format-Table -Property Name, CommandType, Module
For quick interactive use, you can shorten the command, by using aliases and positional parameter binding:
gcm | ft name, commandtype, module
However, note that Format-* cmdlets are only suited to for-display formatting, not for outputting data suitable for later programmatic processing - see this answer and the Select-Object solution below.
By contrast, if you need to construct new objects that have only a subset of the properties of the input objects (while possibly adding new properties / transforming or renaming existing ones via calculated properties[1]), you can use the Select-Object cmdlet:
Get-Command | Select-Object -Property Name, CommandType, Module
Note: If you print this command's output to the console (host), it will result in the same display output as the Format-Table call above, because PowerShell implicitly uses Format-Table formatting for objects that have 4 or fewer (public) properties.[2]
However, for explicit control of display formatting - such as if you want 5-property objects to be shown in tabular format too - Format-* cmdlets must be used.
[1] You may also use calculated properties with Format-Table, among other cmdlets.
[2] 5 or more properties result in Format-List formatting; Generally, this implicit formatting applies only to .NET types that do not have predefined formatting data associated with them; if they do, that data controls the default view and other aspects - see this answer.
Try piping it to select object
get-command|select-object -expandpoperty Name, CommandType, and Module

Why does "Get-Vm | Sls PoweredOff" not grep for VMs that are Powered off?

I am trying to use PowerCLI to search for a list of PoweredOff VMs.
I want to search the results of the command Get-Vm:
Vm01 PoweredOn 1 16
Vm02 PoweredOff 1 16
etc.
I want to search this list for "PoweredOff", but the PowerShell Sls doesn't seem to work if I type:
Get-Vm | sls PoweredOff
It will not show the PoweredOff virtual machines. Can anyone provide any guidance on outputting this a stream of text to search (rather then a list of Objects to search)?
PowerShell cmdlets return objects, not simple text output. You filter output by the values of specific properties with the Where-Object cmdlet.
Get-Vm | Where-Object { $_.PowerState -eq 'PoweredOff' }
Tabular or list output will normally show you the property names. However, not all of the objects' properties may be displayed by default, and sometimes the default output format of a particular type is made to look differently than the output would normally do (e.g. Get-Process output). You can get a list of all properties (and methods) of an object by using the Get-Member cmdlet. Add the parameter -Force to include intrinsic properties. Add the parameter -Static to show class methods instead of object methods.
For VMware's cmdlets you could also check the PowerCLI documentation, which lists the return types of the cmdlets.

Passing subset of objects down pipeline, based on count of properties?

I need to script up some things in PowerCLI (VMWare's bolt on to PowerShell). Basically we have a server cluster with three hosts. Each host has multiple virtual switches. Each virtual switch has multiple vlans ('port groups' in VMWare speak). I need to audit the fact that the same port groups exist on each host (so things keep working if the VM is moved).
Step 1 to achieving this is would be to know that the port group name exists on each of the three host machines.
I'm falling over with how to filter some objects out of all the ones returned by a cmdlet, based on number of results returned from a property of those objects. I then need to perform further operations with original object type that passes the filter test to go on down the pipeline.
To give some specifics, this an example showing 'Some PortGroup Name' and the three hosts it exists on (and as a bonus, the vSwitch):
Get-VirtualPortGroup -Name 'Some PortGroup Name' |
Select-Object Name, VMHostID, VirtualSwitchId
produces the output
Name VMHostId VirtualSwitchId
---- -------- ---------------
Some PortGroup Name HostSystem-host-29459 key-vim.host.VirtualSwitch-vSwitch6
Some PortGroup Name HostSystem-host-29463 key-vim.host.VirtualSwitch-vSwitch6
Some PortGroup Name HostSystem-host-29471 key-vim.host.VirtualSwitch-vSwitch6
Instead of 3, I'm starting with the 1849 port group names that are being returned by Get-VirtualPortGroup. I need a pipeline to whittle the number of VirtualPortGroup objects down to a collection consisting of only those objects where a count of the 'VMHostId' property is less than 3, and pass the remaining VirtualPortGroup objects down the pipeline for further processing.
This seems simple enough to do. I'm still failing though.
The following almost works. Piping it to measure shows a count of 229, instead of the original 1849 (so it's definitely filtered a lot out, and is possibly correctly returning the subset I'm after...?). The problem is, the object type is now a 'Group' or something at this point in the pipeline, and doesn't have all the properties and methods of the original Get-VirtualPortGroup objects.
Get-VirtualPortGroup |
Group-Object -Property Name |
Where-Object $_.Count -lt 3
Bolting a | Select-Object -ExpandProperty Group to the end of the above seemed promising, except it then seems to return the entire collection of Get-VirtualPortGroup objects as though I had done no filtering in there at all....
Am I doing something fundamentally wrong?
How can I filter out objects based on the count of the number of results returned by a specific property of an object, but still pass the original object type down the pipe?
Your approach is correct, but you got the Where-Object syntax wrong. The abbreviated syntax is:
Where-Object <property> <op> <value>
without the current object variable ($_). In your case that would be:
Where-Object Count -lt 3
Otherwise you must use scriptblock notation:
Where-Object { $_.Count -lt 3 }
This should do what you want:
Get-VirtualPortGroup |
Group-Object -Property Name |
Where-Object { $_.Count -lt 3 } |
Select-Object -Expand Group

powershell desc analogy - list output columns and their types

If there anything like SQL's describe so I don't have to look through a "blanket" of | select * output before actually selecting the few columns that I need?
E.g. Get-Process's possible output columns include Id, Name,VirtualMemorySize as well as about three dozen others. I want to get that list of column names, preferrably with their types.
If this for human viewable output then use Format-Table e.g.
Get-Process | Format-Table Name,Id,PM -Auto
Note: if you have lots of output do not use the -AutoSize parameter as that buffers up all its input to determine the optimal size of each column. If you don't specify that parameter, PowerShell will split up the screen space evenly based on number of properties selected but it will display each object as soon as it is received.
For the "column" names, that is bit trickier. You can easily get the list of "ALL" the property names e.g.:
Get-Process | Get-Member -MemberType Properties
If you want just the properties that would normally be display by Format-Table then you need to inspect the View Definition for the System.Diagnostics.Process type in C:\Windows\System32\WindowsPowerShell\v1.0\DotNetTypes.format.ps1xml.