Introduction to MSH

System administration has always been Windows' Achilles' heel. The graphical tools that simplify basic chores just get in the way when there's heavy lifting to be done. And CMD.EXE, the hapless command shell, pales in comparison to the Unix shells that inspired it. Win32 Perl has been my ace in the hole, combining a powerful scripting language with extensions that can wield Windows' directory, registry, event log, and COM services. But I've always thought there should be a better way.

Jeffrey Snover thought so, too. He's the architect of Monad, aka MSH (Microsoft Shell), the radical new Windows command shell first shown at the Professional Developers Conference last fall.

...

MSH is quirky, complex, delightful, and utterly addictive. You can, for example, convert objects to and from XML so that programs that don't natively speak .Net can have a crack at them. There's SQL-like sorting and grouping. You write ad hoc extensions in a built-in scripting language that feels vaguely Perlish. For more permanent extensions, called cmdlets, you use .Net languages.

With MSH, Windows system administration manages to be both fun and productive. And the story will only improve as the .Net Framework continues to enfold Windows' management APIs. Competitors take note: Windows is about to convert one of its great weaknesses into a strength. [Full story at InfoWorld.com]

Here's a sequence of examples to give you a bit of the flavor of MSH. First, a raw process dump:

MSH> get-process
 
Handles  NPM(K)    PM(K)      WS(K) VS(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    105       5     1224         88    32     0.11   1956 alg
     41       2      816        180    23     0.09   1340 aspnet_admin
     69       3     2336         68    36     0.86    760 cmd
     35       2      860        148    27     0.23   1352 Crypserv
    539       6     1868       2312    27    44.89    556 csrss
    100       5      960       1308    29     1.30   1252 ctfmon
 
...etc...

The console output doesn't show everything that's in the returned set of System.Diagnostics.Process objects, but here's one way to see more:

MSH> get-process | get-member -p
 
Class       Name                      MemberData
-----       ----                      ----------
Property    __NounName                System.String
Property    BasePriority              System.Int32
Property    Company                   System.Management.Automation.MshObject
Property    Container
Property    CPU                       System.Management.Automation.MshObject
Property    Description               System.Management.Automation.MshObject
 
...etc...

Alternatively:

MSH> get-process | out-grid

Or:

MSH> get-process | out-excel

Here are processes using more than 15MB of virtual memory:

MSH> get-process | where { $_.virtualmemorysize -gt 150000000}
 
Handles  NPM(K)    PM(K)      WS(K) VS(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
   2411      89    74680      42572   211 1,176.00    396 firefox
    395      11    32720      14848   272    79.89   3504 msh
    484      18    30180      16012   235   337.41   2068 OUTLOOK
    398      16    19172       1220   173    12.33   3636 WINWORD

Grouped by vendor:

MSH> get-process | where { $_.vs -gt 150000000} | group-object company
 
Count Name                      Group
----- ----                      -----
    1 Mozilla                   {firefox}
    1 -                         {msh}
    2 Microsoft Corporation     {OUTLOOK, WINWORD}

Just selected properties:

MSH> get-process | where { $_.vs -gt 150000000} | pick-object name, path, id, vs
  
name                 path                                 id                  vs
----                 ----                                 --                  --
firefox              C:\\firefox.09\\fi...                 396           220983296
msh                  C:\\Program Files...                3504           285908992
OUTLOOK              C:\\PROGRA~1\\MICR...                2068           245936128
WINWORD              C:\\Program Files...                3636           181805056

Ordered by virtual memory size:

MSH> get-process | where { $_.vs -gt 150000000} | pick-object name, path, id, vs |
sort-object vs -d
  
name                 path                                 id                  vs
----                 ----                                 --                  --
msh                  C:\\Program Files...                3504           285908992
OUTLOOK              C:\\PROGRA~1\\MICR...                2068           245936128
firefox              C:\\firefox.09\\fi...                 396           220983296
WINWORD              C:\\Program Files...                3636           181805056

Results assigned to a variable:

MSH> $v = get-process | where { $_.vs -gt 150000000} | pick-object name, path, id, vs |
sort-object vs -d
 
MSH> $v
 
name                 path                                 id                  vs
----                 ----                                 --                  --
msh                  C:\\Program Files...                3504           285908992
OUTLOOK              C:\\PROGRA~1\\MICR...                2068           245936128
firefox              C:\\firefox.09\\fi...                 396           220983296
WINWORD              C:\\Program Files...                3636           181805056
 
MSH> $v[0].id
 
3504

Results as XML:

MSH> get-process | pick-object name,vs | where { $_.vs -gt 150000000} | convert-xml
 
<?xml version="1.0" encoding="utf-16" standalone="yes"?>
<MshObjects xmlns="http://msh">
  <MshObject ReferenceID="ReferenceId-0" Version="1.1" 
          xmlns="http://schemas.microsoft.com/msh/2004/04">
    <MemberSet>
      <Note Name="name" IsHidden="false" IsInstance="true" IsSettable="true">
        <string>firefox</string>
      </Note>
      <Note Name="vs" IsHidden="false" IsInstance="true" IsSettable="true">
        <int>220983296</int>
      </Note>
    </MemberSet>
  </MshObject>
 
...etc...

Preview a request to kill processes using more than 20MB of virtual memory:

MSH> get-process | where { $_.vs -gt 200000000} | stop-process -whatif
# stop-process on firefox (Mozilla Firefox)
# stop-process on msh (Windows command shell preview)
# stop-process on OUTLOOK (Inbox - Microsoft Outlook)

Former URL: http://weblog.infoworld.com/udell/2004/11/02.html#a1106