powershellformattingformattable

Controlling column widths with Format-Table


I am trying to get the last reboot time of some PCs from a list. When I use

foreach ($pc in $pclist) {
  Get-CimInstance -ClassName win32_operatingsystem -ComputerName $pc |
    select csname, lastbootuptime 
}

The output comes as following.

csname       lastbootuptime
------       --------------
CONFA7-L1-1A 7/15/2016 9:55:16 AM
CONFA7-L1-1F 5/31/2016 8:51:46 AM
CONFA7-L1-1G 6/18/2016 11:09:15 AM
CONFA7-L1... 6/26/2016 5:31:31 PM
CONFA7-L3... 7/24/2016 3:48:43 PM

Which is neat, but if the PC name is long, I am unable to see the full name. So I pipelined Format-Table:

Get-CimInstance -ClassName win32_operatingsystem -ComputerName $pc |
  select csname, lastbootuptime |
  Format-Table  -HideTableHeaders 

And this is what I get:

CONFA7-L1-1A 7/15/2016 9:55:16 AM



CONFA7-L1-1E 7/21/2016 12:58:16 PM



CONFA7-L1-1F 5/31/2016 8:51:46 AM

There are two problems here.

  1. There is no heading. If I remove -HideTableHeaders there will be heading for every output which is not required.

  2. There is a lot of white spaces in between.

Basically I just need to get an output similar to the first one, but without truncating the full names. How can I fix these?


Solution

  • To summarize and complement the helpful comments made by PetSerAl and Ansgar Wiechers:

    tl;dr

    Get-CimInstance -ClassName win32_operatingsystem -ComputerName $pclist |
      Sort-Object CSName |
        Format-Table CSName, LastBootUpTime -AutoSize
    

    -AutoSize is what ensures that the CSName (computer name) column is as wide as it needs to be to show all values in full (unless these values themselves are too long to fit on a single line, in which case -Wrap must be used - see below).

    Get-CimInstance takes an array of computer names, so there's no need for a loop; however, since the target computers are queried in parallel, the order of objects returned will typically not match the input order of computer names - this is rectified with the Sort-Object CSName call.

    To control the width of individual columns:

    # Instead of a simple property name, 'prop1', pass a *hashtable*
    # (@{ ... }`) with a 'width' entry to Format-Table
    PS> [pscustomobject] @{ prop1='1234567890'; prop2='other' } |
           Format-Table -Property @{ e='prop1'; width = 5 }, prop2
    
    prop1 prop2
    ----- -----
    1234… other
    

    Note: In Windows PowerShell, you'll see just 12... as the truncated value, because it uses 3 individual . characters to represent the truncation; in PowerShell [Core] 6+ this was improved to using a single character, (HORIZONTAL ELLIPSIS, U+2026).

    Read on to learn more about table formatting.


    At its core, your question is about how to control the output column width of tabular output, which applies to any cmdlet's output.

    Use the Format-Table cmdlet (directly) for tabular output, not Select-Object: the purpose of Select-Object is to create custom objects, not to format output; if such objects (generally, instances of any type without predefined formatting views) happen to haven 4 or fewer properties, they are by default formatted with Format-Table behind the scenes (but you don't get to apply options); otherwise, it is Format-List that is implicitly used. Thanks, PetSerAl.


    [1] As zett42 points out, you can technically bypass this requirement if all columns are to have the same custom width, because passing a property-name string to the e (Expression) hashtable entry is interpreted as a wildcard pattern, so that string '*' matches all property names; e.g.:
    [pscustomobject] @{ a=1; b=2 } | Format-Table @{ e='*'; width=10 }