Home > Software design >  Not sure why calling a variable changes the output
Not sure why calling a variable changes the output

Time:04-22

So here's what's up. I'm in the process of troubleshooting some code, and I'm noticing an odd behavior. If I use the following code:

$UpdateHistory = Get-WUHistory -last 1
$TodaysDate = (Get-Date)

$UpdateHistory.Date
$TodaysDate.DateTime

I get the output:

Sunday, April 17, 2022 10:01:13 AM
Thursday, April 21, 2022 11:23:00 AM

However, if I use this code:

$UpdateHistory = Get-WUHistory -last 1
$TodaysDate = (Get-Date)

$UpdateHistory
$UpdateHistory.Date
$TodaysDate.DateTime

I get this output:

ComputerName Operationname  Result     Date                Title
------------ -------------  ------     ----                -----
RedactedComputerName Installation   Succeeded  4/17/2022 3:01:1... Windows Malicious Software Removal Tool x64 - v5.100 (KB890830)

Date        : 4/17/2022 12:00:00 AM
Day         : 17
DayOfWeek   : Sunday
DayOfYear   : 107
Hour        : 10
Kind        : Unspecified
Millisecond : 0
Minute      : 1
Month       : 4
Second      : 13
Ticks       : 637857864730000000
TimeOfDay   : 10:01:13
Year        : 2022
DateTime    : Sunday, April 17, 2022 10:01:13 AM

Thursday, April 21, 2022 11:23:56 AM

What the heck is going on? The reason for the $Updatehistory on line 4 was simply to see a list of the entries, but it appears to be fundamentally changing the object. It's throwing me for a loop.

CodePudding user response:

This question is similar to Why is the output format changed when running two PowerShell commands in one line?

Long story short: The first object output in the pipeline may affect how subsequent objects are presented, especially if they are not of the same type.

To get consistent output, either explicitly convert it prior to output, or pipe the output to format-table or format-list.

CodePudding user response:

It's not changing the object, it is changing how the object is displayed:

Slartiprefect's answer provides a great, concise summary; let me dig a little deeper:

Specifically, you're seeing the following behavior of PowerShell's for-display output formatting system:

  • You're outputting multiple objects of disparate types to the host (display).

  • The first output object, $UpdateHistory, contains a data type for which explicit formatting data is defined, and that data defaults to a table view (implicit Format-Table use).

  • Because the second output object is of a different type ([datetime]), to which the first object's formatting data cannot be applied, PowerShell defaults to Format-List formatting (as if you had called $UpdateHistory.Date | Format-List)

    • Caveat:
      • If the first output object (of a non-primitive type) has no formatting data associated with its type, PowerShell defaults to Format-Table formatting if the object has 4 or fewer (public) properties, and to Format-List for 5 or more, showing all those properties.

      • When Format-Table formatting is chosen, the display columns are locked in based on that first object's properties.

      • If subsequent objects don't have these properties, they may be appear to be missing from the output altogether: they are still there, but produce no visible output.

      • A simple example with a [pscustomobject] instance followed by a [datetime] instance (the [pscustomobject] type has no predefined formatting data associated with it):

        # !! Note how the Get-Date output appears to be missing.
        PS> [pscustomobject] @{ one = 1; two = 2; three = 3 }; Get-Date
        
        one two three
        --- --- -----
          1   2     3
        
      • See this answer for more information.

  • The third output object is of yet another type, [string], and strings always print the same, as their value.


The solution is to force instant formatted output to the host separately for at least the first object in your case, which is easiest with Out-Host:

$UpdateHistory | Out-Host # Force instant formatted output to the host.
$UpdateHistory.Date
$TodaysDate.DateTime

However, note that this bypasses the success output stream so that the objects sent to the display this way are not part of your script's data output any more.

Similarly, if you were to use a Format-* cmdlet instead of Out-Host, it is objects representing formatting instructions that are sent to the success output stream; while they render as expected in the host (display), they are useless as data. (You may, however, combine formatting cmdlets with Out-Host; e.g. Get-ChildItem | Format-Table | Out-Host).

  • Related