powershell desc analogy - list output columns and their types - powershell

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.

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

What causes the output of select-object to be truncated?

I have the following silly PowerShell script:
$username = 'rny'
$null = mkdir "c:\Users\$username\YYY"
$null = mkdir "c:\Users\$username\YYY\TODO"
$null = mkdir "c:\Users\$username\YYY\TODO\2021-12-22_Foo-bar-baz-etc"
$files = "C:\Users\$username\one-two-three-four.sql.wxyz",
"C:\Users\$username\YYY\TODO\2021-11_29_abcdefghijklmnop.wxyz",
"C:\Users\$username\YYY\TODO\2021-12-22_Foo-bar-baz-etc\another-filename.wxyz"
foreach ($file in $files) {
$null = new-item $file
}
Get-ChildItem . -errorAction silentlyContinue -recurse -filter *.wxyz | select-object fullName
foreach ($file in $files) {
remove-item -literalPath $file
}
rmdir "c:\Users\$username\YYY\TODO\2021-12-22_Foo-bar-baz-etc"
rmdir "c:\Users\$username\YYY\TODO"
rmdir "c:\Users\$username\YYY"
When I execute it, the output of the get-childItem ... | select-object pipeline is truncated:
FullName
--------
C:\Users\rny\one-two-three-four.sql.wxyz
C:\Users\rny\YYY\TODO\2021-11_29_abcdefghijklmnop.wxyz
C:\Users\rny\YYY\TODO\2021-12-22_Foo-bar-baz-etc\an...
Note especially the last line. This behaviour was noted elsewhere on SuperUser and the accepted answer is to pipe the output into format-table with -autoSize. So far, so good.
However, If I comment the second file in the assignment of the $files array like so
$files = "C:\Users\$username\one-two-three-four.sql.wxyz",
# "C:\Users\$username\YYY\TODO\2021-11_29_abcdefghijklmnop.wxyz",
"C:\Users\$username\YYY\TODO\2021-12-22_Foo-bar-baz-etc\another-filename.wxyz"
the output is not truncated anymore:
FullName
--------
C:\Users\rny\one-two-three-four.sql.wxyz
C:\Users\rny\YYY\TODO\2021-12-22_Foo-bar-baz-etc\another-filename.wxyz
This puzzles me because the name of the file that was truncated is now fully visible and I have no explanation for this.
So, what exactly causes the truncation of the file in one case and not in the other case?
This isn't so much to do with Select-Object per se - it's more to do with how PowerShell converts values into string representations, and specifically in this case how it does that when it displays uncaptured output from cmdlets on the console.
PowerShell (Windows and Core) has a bunch of preconfigured "views" that define how some built-in types are rendered - e.g. whether they use Format-List or Format-Table, what properties to display, and in the case of tables, how wide to display each column - see about_Format.ps1xml.
For other types, PowerShell tries to make a best-guess on the fly. To do that it waits for the first N items in arrive from the input to make a decision on the formatting rules to apply. I can't find any definitive documentation that says how many items PowerShell waits for, so that might be a good follow-up question :-).
And you can obviously override these defaults by passing formatting parameters for Format-Table and Format-List.
In your case the top-level script has received pipeline output containing an array of PSCustomObject objects (i.e. the output from Select-Object) and it's decided to show them in a table with a column for the FullName property.
Example 1
In your first example, it's looked at the first two PSCustomObject items and decided to make the FullName column 54 characters wide since that's the length of C:\Users\rny\YYY\TODO\2021-11_29_abcdefghijklmnop.wxyz, and the third item gets truncated to that same width (if you include the ...) because it wasn't included in the decision-making process for column widths.
FullName
--------
C:\Users\rny\one-two-three-four.sql.wxyz
C:\Users\rny\YYY\TODO\2021-11_29_abcdefghijklmnop.wxyz
C:\Users\rny\YYY\TODO\2021-12-22_Foo-bar-baz-etc\an...
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^| 54 characters
Example 2
In your second example, PowerShell sees the longest FullName property in the first couple of PSCustomObjects is C:\Users\rny\YYY\TODO\2021-12-22_Foo-bar-baz-etc\another-filename.wxyz and so uses a column width of 70.
FullName
--------
C:\Users\rny\one-two-three-four.sql.wxyz
C:\Users\rny\YYY\TODO\2021-12-22_Foo-bar-baz-etc\another-filename.wxyz
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^| 70 characters
Example 3
Finally, if you do what #notjustme suggests in the comments and add -ExpandProperty FullName onto Select-Object you get an array of string values instead of an array of PSCustomObjects which is why you might see PowerShell apply different formatting rules - you don't get a FullName header for example because the values are strings not objects with properties, and it's using Format-List instead of Format-Table.
C:\Users\rny\one-two-three-four.sql.wxyz
C:\Users\rny\YYY\TODO\2021-12-22_Foo-bar-baz-etc\another-filename.wxyz
To add some background to mcclayton's helpful answer:
Specifically, you're seeing the effects of the infamous 300-msec. delay built into Format-Table formatting, which PowerShell implicitly applies to instances of .NET types that have 4 or fewer properties and do not have explicit formatting data associated with them.
See this answer for details (which is given in the context of a different symptom of the same problem, namely unexpected output ordering), but the short of it is: The delay is used to infer suitable column widths from the specific property values received within the delay period.
This means that objects with property values received after the 300-msec. delay may be truncated in their column display if their values happen to be wider than the widest among the values received during the delay period.
Specifically, your symptom implies that only the first two objects were received within the delay period, and that the longer among the two property values then locked in the column width; when the third object was received later, the column width was already locked in, and the longer value was truncated (indicated with trailing ... in Windows PowerShell (3 . chars.) and … in PowerShell (Core) 7+ (single char))
The only way to avoid truncating is to know the max. column width ahead of time and pass it to an explicit Format-Table call -
notably, this prevents using the output as data. See below.
Here's a simple way to provoke the problem:
Note: The Select-Object calls below aren't strictly needed, but are provided for symmetry with the question.
# Create blocks of two objects with strings of different length in their
# .Prop value: 10 chars. vs. 100 chars.
$count = 10000 # How often to repeat each object in a row.
$objs =
(, [pscustomobject] #{ Prop = ('x' * 10) } * $count) +
(, [pscustomobject] #{ Prop = ('y' * 100) } * $count)
# Depending on the value of $count - which translates into how
# long it takes until the second block of objects starts emitting -
# truncation will occur or not.
$objs | Select-Object Prop
With blocks of 10,000 objects, I do see the truncation: it takes long enough for the first block - with the short property value - to lock in the width of the display column, causing the objects in the second block to be truncated:
Prop
----
xxxxxxxxxx
...
yyyyyyyyy… # <- truncated, because width 10 was locked in during the delay
...
To prevent truncation, pass a calculated property to Format-Table specifying the max. width:
$objs | Select-Object Prop | Format-Table #{ n='Prop'; e='Prop'; width = 100 }

How do I run multiple lines of code in PowerShell? [duplicate]

I'm getting unexpected results when executing two PowerShell commands separated by a semicolon. The output of the second command changes. If I run them in reverse order, I don't see the second command output.
Here I'm simply trying to get a time stamp and a list of groups a user belongs to in AD, as a one-liner.
If I run this line, i get the following output:
Get-ADPrincipalGroupMembership username | Select-Object name
name
----
Domain Users
CL-Inventory-Group
...
However if I run the following, this behavior changes:
get-date; Get-ADPrincipalGroupMembership username | Select-Object name
Wednesday, April 3, 2019 2:31:35 PM
name : Domain Users
name : CL-Inventory-Group
...
Stranger yet, If i run the in reverse, meaning i say get-date after the first command, the date stamp never shows up after the groups are listed.
Am I improperly separating commands?
tl;dr:
Submitting multiple ;-separated commands at the prompt (interactively) still sends their output to a single pipeline (you can think of each command line submitted as an implicit script file).
In short: in your case, the first command output's automatic display formatting also determined the display formatting for the 2nd command, so which command comes first matters:
get-date; Get-ADPrincipalGroupMembership username | Select-Object name
locked in implicit use of Format-List for all output following Get-Date, which explains the each-property-on-its-own line output from the Get-ADPrincipalGroupMembership ... command.
If i run the reverse, meaning i say get-date after the first command, the date stamp never shows up after the groups are listed.
Select-Object output instances of type [pscustomobject], which, due to their having just 1 property in this case, locked in tabular display, i.e., implicit use of Format-Table, with the selected property as the only column, i.e., just Name here. Since the [datetime] type output by Get-Date doesn't have a Name property, Get-Date's output was effectively invisible.
Read on for background information and the complete rules.
PowerShell's default display formatting is optimized for objects of the same type, as that is the typical case.
If a pipeline contains a mix of types, the specific formatting that results by default depends on:
the order of objects in the pipeline
and their default formatting behavior
See the next section for details.
You can use explicit Format-* calls to control the formatting; in your case, you can use Format-Table on your 2nd command to force tabular output:
Get-Date; Get-ADPrincipalGroupMembership username | Select name | Format-Table
Caveat: The output from Format-* cmdlets are formatting instructions rather than the original data, which makes this output unsuitable for further programmatic processing.
How PowerShell formats objects of different types in the same pipeline for display:
In the absence of explicit formatting commands (Format-Table, Format-List, ...), PowerShell automatically chooses a suitable display format, based on a given object's type:
If present for a given type, PowerShell uses predefined formatting instructions (see Get-Help about_Format.ps1xml)
In their absence:
If the type is a primitive type (see below): the object's .ToString() representation is output.
Otherwise: the format style is chosen based on the following simple rules: 4 or fewer properties? -> Format-Table; 5 or more? -> Format-List.
Note: Primitive is used loosely here to refer to:
all primitive CLR types - those for which .IsPrimitive returns $true, namely [Boolean], [Byte], [SByte], [Int16], [UInt16], [Int32], [UInt32], [Int64], [UInt64], [IntPtr], [UIntPtr], [Char], [Double], [Single]
types [decimal], [bigint], and [securestring]
[string] (strings always print as themselves, in full)
any other property-less types.
If all objects in a pipeline are of the same type, the above by definition applies to all of them.
By contrast, if there is a mix of types in the pipeline, the following logic applies:
Any instances of primitive types always print and always print the same, namely as a representation of the single value that they are (not an as an object with properties), obtained via a call to their .ToString() method; e.g., 12 or 3.0 or hi; primitive types have no bearing on the formatting of subsequent objects in the pipeline.
The first non-primitive object in the pipeline:
is itself printed based on either its predefined formatting instructions or the default rules stated above (based on the number of properties).
locks in the format style - list vs. table - for all remaining non-primitive objects:
If the object itself implicitly uses Format-Table or Format-List, so will all remaining non-primitive objects.
If the object implicitly uses Format-Custom (e.g, in the case of Get-Date, via predefined formatting), it is Format-List that is locked in.
All subsequent non-primitive objects then use the locked-in format style.
Caveat: If Format-Table is locked in, the first non-primitive object alone determines the set of properties displayed as table columns:
If the first non-primitive object has no formatting data associated with it - which notably applies to [pscustomobject] - it
can cause subsequent non-primitive objects to seemingly disappear if they don't have these properties - such objects are still in the output stream, however, they're just not displayed - see this answer for a demonstration.
GitHub issue #7871 proposes at least issuing a warning when that happens.
If the first non-primitive object has formatting data associated with it (as reported by Get-FormatData), all subsequent non-primitive objects of a different type are formatted with Format-List. That is, you'll get a mix of tabular and list-style output.
On a side note: Since PSv5, implicit use of Format-Table results in asynchronous behavior that may be surprising; see this answer.
If it is Format-List that is locked in, no information is "lost", as each object's properties are then listed individually, on their own lines.
When the powershell console formatter sees objects of multiple types, it will default to outputting based on the first element output. For Date it output as a list, for the custom object output from Select-Object, it's a table. How the output is formatted depends on the type of the object itself (see help About Format.ps1xml). You can force the output of Select-Object to be a table using Format-Table:
get-date; Get-ADPrincipalGroupMembership username | Select-Object name | Format-Table

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

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.