Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the output format changed when running two PowerShell commands in one line?

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?

like image 608
Paweł Czopowik Avatar asked Apr 03 '19 18:04

Paweł Czopowik


2 Answers

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.

      • 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.

like image 135
mklement0 Avatar answered Oct 12 '22 23:10

mklement0


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
like image 34
zdan Avatar answered Oct 12 '22 23:10

zdan