O'Reilly logo

Monad (AKA PowerShell) by Andy Oakley

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Display Data

So far, we've been building commands with little attention to the output, other than assuming it will be displayed in the console window. How does MSH know how to neatly display the objects, especially if it's not familiar with their structure? It turns out that there is a fair bit of plumbing in MSH to make sure that console output is displayed in a useful form for a wide range of types.

The default formatter is a standard part of the shell. If and when an object reaches the end of the pipeline, MSH inspects its type and compares it to a list of known objects. If a match is found, MSH understands how to best format the object for display; if not, MSH displays the .NET properties of the object.

The type of the first object to reach the end of the pipeline generally governs how subsequent objects will be displayed. Alternatively, a command or script can explicitly insert formatting cmdlets (from the format-* family) and/or outputting cmdlets (from the out-* family) in the pipeline for more explicit control over how and where objects should be displayed.

Let's take a look at some of the different tools available for presenting data.

How Do I Do That?

Armed with the knowledge that there is a default formatter at work, we can look at the output of get-process in a new light. The default formatter knows about Process objects because they are one of the common data types used in shell work. The default formatter is configured to pick out some of the more interesting aspects of a process and tabulate them for display:

    MSH D:\MshScripts> get-process

    Handles  NPM(K)    PM(K)      WS(K) VS(M)   CPU(s)     Id ProcessName
    -------  ------    -----      ----- -----   ------     -- -----------
        123       5     1008       2500    32     1.24   1844  alg
        799      14    14188      16480    71   189.09   1656  CcmExec
         19       1     1444       1516    13     1.61   1052  cmd
        426       5     1796       3148    24   184.88    464  csrss
        361      11     9420      13312    58 2,206.90    212  explorer
    ...

Some of the columns in this output, such as ProcessName and Id, are simple properties of the Process data structure. Others, including the non-paged memory size, paged memory size, and working set, are calculated columns . For each of these columns, the default formatter is running a small fragment of script on each Process object it sees, in this case, to convert a number in bytes to kilobytes for display.

We'll come back to the default formatter's behavior shortly. Meanwhile, let's say we're just interested in displaying a subset of the properties of the msh process. The format-list cmdlet is one of the simplest formatters. When provided with a list of property names, it will display each in sequence, along with its corresponding value. For now, let's just use a few properties that are common to all Process types; we'll see where these property names come from later when we meet the get-member cmdlet in Chapter 3:

    MSH D:\MshScripts> get-process msh | format-list
    ProcessName,PriorityClass,Id,VirtualMemorySize,Handles,StartTime,WorkingSet,Modules

    ProcessName       : msh
    PriorityClass     : Normal
    Id                : 3088
    VirtualMemorySize : 186503168
    Handles           : 451
    StartTime         : 1/22/2005 5:20:51 PM
    WorkingSet        : 37068800
    Modules           : {msh.exe, ntdll.dll, mscoree.dll, KERNEL32.dll,
                        ADVAPI32.dll, RPCRT4.dll, SHLWAPI.dll, GDI32.dll,
                        USER32.dll, msvcrt.dll, mscorwks.dll, MSVCR80.dll,
                         mscorlib.ni.dll, ole32.dll, shell32.dll, comctl32.dll,
                        comctl32.dll, rsaenh.dll, mscorsec.dll, WINTRUST.dll,
                        CRYPT32.dll, MSASN1.dll, IMAGEHLP.dll, SOFTPUB.DLL,
                        xpsp2res.dll, userenv.dll, VERSION.dll, secur32.dll,
                        netapi32.dll, cryptnet.dll, WLDAP32.dll, WINHTTP.dll,
                        SensApi.dll, Cabinet.dll, System.Management.Automation.
    ...

format-table is one of the more versatile formatters. In simple usage it can be provided with a comma-separated list of properties and will automatically tabulate the corresponding values for each object it sees. In many cases, a simple format-table pass will be sufficient for data presentation:

    MSH D:\MshScripts> get-process | format-table Id,ProcessName,StartTime

                       Id ProcessName                StartTime
                       -- -----------                ---------
                     1844 alg                        1/22/2005 5:17:58 PM
                     1656 CcmExec                    1/22/2005 5:17:56 PM
                     1052 cmd                        1/29/2005 5:32:20 PM
                      464 csrss                      1/22/2005 5:17:34 PM
                      212 explorer                   1/22/2005 5:19:06 PM
                        0 Idle
                      544 lsass                      1/22/2005 5:17:36 PM
                     3088 msh                        1/22/2005 5:20:51 PM
    ...

As with most formatting cmdlets, format-table will accept a GroupBy parameter. When displaying results, this is used to separate objects based on some property value. For cleanliness of presentation, it's common to first sort the input before presentation so that each grouped category appears just once. For example, let's create a tabulated process listing grouped by process priority:

    MSH D:\MshScripts> get-process | sort-object PriorityClass,ProcessName
    | format-table -GroupBy PriorityClass ProcessName,Id,VirtualMemorySize,PriorityClass


        PriorityClass: Normal

    ProcessName                                   Id         VirtualMemorySize
    -----------                                   --         -----------------
    svchost                                      896                  29769728
    svchost                                      756                  35753984
    svchost                                      824                 110878720
    svchost                                      720                  59514880
    smss                                         308                   3911680
    spoolsv                                     1088                  27459584
    svchost                                      940                  39124992
    wmiprvse                                     320                  23642112
    wmiprvse                                    1920                  26124288
    wuauclt                                     1708                  68648960
    System                                         4                   1941504
    csrss                                        464                  25485312
    explorer                                     212                  60772352
    alg                                         1844                  33259520
    CcmExec                                     1656                  73891840
    cmd                                         1052                  13979648
    notepad                                     1792                  25735168
    services                                     532                  25305088
    msh                                         3088                 186503168
    lsass                                        544                  43470848


        PriorityClass: High

    ProcessName                                   Id         VirtualMemorySize
    -----------                                   --         -----------------
    winlogon                                     488                  53092352
    Idle                                           0                         0

We've only scratched the surface of format-table here. It's possible to be far more expressive in terms of formatting (width and alignment), as well as content (through the use of calculated columns). In the following example, the syntax may seem somewhat obtuse to begin with, but it's very regular and can be used to create almost any imaginable table output:

    MSH D:\MshScripts> get-process | format-table @{expression="ProcessName";
    width=50; label="Name"}, @{expression="Id"; width=5}, @{expression={$_
    .VirtualMemorySize/1024}; width=30; label="Virtual memory (kb)"}

    Name                                             Id    Virtual memory (kb)
    ----                                             --    -------------------
    alg                                            1844                  32480
    CcmExec                                        1656                  72008
    cmd                                            1052                  13652
    csrss                                           464                  24888
    explorer                                        212                  59348
    Idle                                              0                      0
    lsass                                           544                  42452
    msh                                            3088                 182132
    notepad                                        1792                  25132
    ...

What Just Happened?

We started by looking at how the default formatter operates on known types. It's important to realize that the formatting process is applied to any object when it has nowhere further to go (i.e., it has reached the end of the pipeline, and the next stop is the console output). The shell does not discriminate between a single command (a pipeline of length 1, if you will) and a complex series of processes all piped together—ultimately, both cases have an end point.

format-list is a simple case, and we've already seen the bulk of its functionality here. One thing to remember is that because it generates the same listing for each element, its output can tend to get very large when it's used for any more than a small number of objects. It has a couple of other interesting usages. For types known to MSH, format-list with no parameter will display the object's most relevant properties, whereas format-list with a wildcard parameter (*) will display all of them. If the default formatter does not recognize a type it encounters, it always falls back to the list formatter that shows all properties and values of the object.

The complex format-table case uses a construct known as a hashtable, which is a data type we haven't discussed yet. When we look at hashtables and other data types in Chapter 3, we'll be able to revisit this aspect of format-table to create some more interesting reports.

What About...

...Are there any other formatters? The format-wide cmdlet can be used to list output in a horizontal order. Its formatting is similar to the effect of the /w switch on the DIR command where items are listed left to right and wrap when a line is complete.

...Combining grouping and the default formatter? It certainly is possible. If a formatting cmdlet is used without parameters and the objects are of a known type, the default formatting rules will be applied. In other words, applying format-table to the output of get-process will result in the familiar tabulated style offered by the default formatter. A pipeline such as get-process | sort-object PriorityClass | format-table -GroupBy PriorityClass can be used to group processes by priority while maintaining the standard column format.

Where Can I Learn More?

You can learn more about how the default formatter works by looking at the *.mshxml file distributed with MSH. This file references a series of similar files, each of which contains configuration that is used by the default formatter.

The get-command cmdlet, as well as the help system, can also be used to find more information about the available formatters and their usage:

    MSH D:\MshScripts> get-command format-*

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required