powershelloutput

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?


Solution

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