When you run a command in Windows PowerShell, objects are written to the pipeline and possibly even passed from one cmdlet or function to the next. If there are any objects left in the pipeline at the end, PowerShell displays them. But sometimes the display is less than optimal. To overcome this, you'll need to take control of PowerShell's formatting system.

How Formatting Works

Without getting too deep into the technical details, here's how PowerShell handles formatting: At the end of the pipeline, PowerShell looks at the remaining objects and determines their types. PowerShell then looks through a set of XML configuration files for instructions on how to format each specific object type. It might see a default display for a table or list. If so, PowerShell passes the objects to the appropriate formatting cmdlet, which displays the results on screen. Or PowerShell might only find a default list of properties. Depending on the number of properties, PowerShell will decide if it needs a table or a list and again call the appropriate formatting cmdlet. As a last resort, if no instructions are found, PowerShell will display all object properties.

You can override this formatting system by explicitly running the formatting cmdlets. The formatting cmdlets that you're most likely to use are Format-Wide, Format-Table, and Format-List.

How to Use the Format-Wide Cmdlet

The Format-Wide cmdlet, which has an alias of fw, will display a single property in wide format. In the Windows Cmd.exe shell, you might have used a command like dir /w to see a wide directory listing. Format-Wide provides the same type of display for any object type. For example, the following command displays the results from the Get-Process cmdlet in wide format:

Get-Process | Format-Wide

You can see the results in Figure 1.

Figure 1: Displaying Results in Wide Format

With Format-Wide, most object types have a defined default property, which is usually a name. However, you can specify a different property. For example, instead of displaying service names in wide format, you can display the DisplayName property in wide format by using the command:

Get-Service | Where-Object {$_.status -eq 'running'} |
  Format-Wide -Property DisplayName

Format-Wide usually does a good job of maximizing the display, but you can fine tune it if you want. For example, when Get-Process was piped to Format-Wide in the first example, the results were in two columns, as shown in Figure 1. You can force Format-Wide to use more columns using the -Column parameter:

Get-Process | Format-Wide -Column 3

Now the results are in three columns, as Figure 2 shows.

Figure 2: Displaying Results in Three Columns

You can force Format-Wide to squeeze as much as it can into the display using the -AutoSize parameter. For example, the following command returns services that start with the letter m, maximizing the amount of data displayed:

Get-Service m* | fw -AutoSize

Notice that in this instance, I'm using the alias fw. Figure 3 shows the results.

Figure 3: Maximizing the Amount of Data Displayed

You might want to combine the auto-size feature with the grouping feature of Format-Wide. When using the -GroupBy parameter, PowerShell will format the output in groups based on a specified property. But be careful. Look at this command:

Get-Service | fw -GroupBy Status -AutoSize

You might think that this command will get the services, then display them in wide format grouped by the Status property. But look at the result in Figure 4. 

Figure 4: Getting Unexpected Results When Grouping Results

It's probably not what you expected. PowerShell took the incoming service objects and formatted them as they arrived. This problem is easily solved by sorting first with the Sort-Object cmdlet, which has an alias of sort:

Get-Service w* | sort Status | fw -GroupBy Status -AutoSize

This command returns only those services starting with the letter w so that you can see all the results, which are shown in Figure 5. In the results, notice that the stopped services are displayed using five columns and the running services are displayed using only four columns. Auto-sizing results will vary depending on the length of each property value.

Figure 5: Getting the Expected Results When Grouping Results

The -GroupBy parameter is supported by all the formatting cmdlets. The -AutoSize parameter is supported by Format-Wide as well as Format-Table, which I'll discuss next.

How to Use the Format-Table Cmdlet

As its name suggests, the Format-Table cmdlet, which has an alias of ft, formats output as a table. Formatting results as a table is commonly done by administrators, and PowerShell even does it by default in some cases. For example, running

Get-Process

produces the same results as running

Get-Process | Format-Table

As I mentioned in the "How Formatting Works" section, PowerShell uses a set of configuration files to determine what to display and how. Some object types have predefined formatted outputs. In this case, the configuration files tell PowerShell to format the process objects as a table. In theory, this means that you don't need to include the Format-Table cmdlet when using Get-Process if you want the results formatted in a table. However, how many times have you run a command only to find that the output isn't exactly optimal? Take, for example, the following command that uses the Select-Object cmdlet, which has an alias of select, to return the ID, Name, and WorkingSet (WS) properties:

Get-Process | Format-Table

As you can see in Figure 6, the output is in a table, but the columns' size and placement aren't ideal.

Figure 6: Selecting Properties Without Any Formatting

PowerShell did the best job it could, but selecting properties isn't the same as formatting them. You can help by piping the results to the Format-Table cmdlet with the -AutoSize parameter to force PowerShell to auto-size the results in the table:

Get-Process | select ID,Name,WS |
  Format-Table -AutoSize

As Figure 7 shows, the output is nicer.

Figure 7: Selecting and Formatting the Properties

If you know from the beginning that you want the output to be formatted in a table, you can skip the Select-Object step and simply specify the properties when calling Format-Table:

Get-Process | Format-Table ID,Name,WS -AutoSize

This will produce the same output. If all you want to do is look at the results, this is perfectly fine. But there's something about formatting that trips up many PowerShell beginners, which I'll discuss in a bit.

Besides -AutoSize, another Format-Table parameter that you'll probably use often is the -View parameter. A view is an alternative layout. Using the -View parameter tells PowerShell to use one of the alternative displays. For example, if you run the following command, you'll get a different set of properties automatically grouped by the PriorityClass property:

Get-Process | sort PriorityClass | ft -View Priority

Notice that I used the alias ft, which you'll probably end up using a lot. Also notice that I sorted first to improve the display output, just as I did when grouping earlier with Format-Wide. Figure 8 should give you an idea of what to expect. You might get errors because some processes like Idle and System are protected. You can ignore the errors.

Figure 8: Using an Alternative Display

Here's another example I'll leave for you to try:

Get-Process | sort StartTime | ft -View StartTime

Like the previous command, this command might generate errors because some processes won't have start times.

Discovering alternative views takes a little detective work. First, you use the Get-Member cmdlet to find the object type. In the case of the last Get-Process example, you'd run:

Get-Process | Get-Member

The results show that the object type is System.Diagnostics.Process. Then, you run the following command to expand the definition property:

Get-FormatData System.Diagnostics.Process |
  Select -ExpandProperty FormatViewDefinition

In the results shown in Figure 9, the Name column contains the view names. The Control column gives you a hint about what formatting cmdlet to use to see the corresponding view. Note that you won't necessarily see views for every object type, so you might still need to do a lot of manual testing.

Figure 9: Discovering the Different Views Available

How to Use the Format-List Cmdlet

Another common way to format output is a list. You can force PowerShell to output a list by using the Format-List cmdlet, which has an alias of fl. For example, if you run the following command, you'll find that PowerShell formats the results as a table:

Get-Process | select ID,Name,WS,VM

If you'd rather have the output formatted as a list, you can pipe the results to Format-List like this:

Get-Process | select ID,Name,WS,VM | Format-List

Figure 10 shows the results.

Figure 10: Formatting Output as a List

Like the other formatting cmdlets, Format-List lets you specify one or more properties to display. You can even use wildcards, including the asterisk (*) to get all the properties.

Although piping Get-Process output to Get-Member can help you discover an object's properties, sometimes it helps even more to see the values for those properties. A great way to use Format-List is to get a single object and list all its properties and their values:

Get-Process winword | fl *

With output like that in Figure 11, you'll have a better idea of what the properties mean and can begin devising a more detailed command.

Figure 11: Listing an Object's Properties and Their Values

As with Format-Table, Format-List might have different views defined for different object types. Take, for example, the command:

Get-Service | fl

The results in Figure 12 show that PowerShell has a view defined for service objects when they're formatted as a list.

Figure 12: Discovering that PowerShell has a View Defined for Service Objects When They're Formatted as a List

To learn about the views available, you can try using Get-FormatData and looking for ListControl in the Control column. Alternatively, you can experiment by running commands piped to Format-Table and Format-List. You might be surprised at what you discover.

Format at the End

You might have noticed that in all my examples, the formatting cmdlet was at the end of the command. In classes and forums, I often see PowerShell novices try to do something like this:

Get-ChildItem C:\scripts\*.ps1 |
  ft name,length,LastWriteTime,CreationTime |
  Export-CSV C:\work\files.csv

But when they look at the .csv file, it's gibberish. Here's another command that will fail, but at least there's an error message that explains why:

Get-ChildItem C:\scripts\*.ps1 |
  ft name,length,LastWriteTime,CreationTime |
  sort Length

Figure 13 shows the error message. Admittedly, this message isn't easy to understand, so I'll explain what it means. When you invoke one of the formatting cmdlets, it creates a different type of object and passes it back to PowerShell. So, in the failed command, Format-Table wrote formatting directive objects to the pipeline, which PowerShell then tried to sort on the Length property. But at this point, the pipeline is no longer working with file objects, so the command fails. This gets confusing because if you replace Format-Table with Select-Object, it works.

Figure 13: Receiving an Error Message

The rule of thumb is that formatting should be the last part of a PowerShell command. As long as you don't try to do anything after a formatting cmdlet, you shouldn't run into any problems. The assumption is that you're formatting to make the results easier to read and nothing else. However, there's one exception. You can pipe any formatted output to any of the Out cmdlets, such as Out-File or Out-Printer. For example, the following command pipes the Format-Table output to Out-File:

Get-ChildItem C:\scripts\*.ps1 |
  ft name,length,LastWriteTime,CreationTime |
  Out-File C:\work\scripts.txt

PowerShell will take the same output you'd see on screen and send it to the file.

Give Them a Try

I recommend that you take a few minutes to read the Help files and look at the examples for the formatting cmdlets, then give them a try. Don't be afraid to experiment. Once you understand how formatting works, you'll be bending PowerShell to your will in no time.