We looked at a lot of CLI models when designing PowerShell.  One of them was netsh.  One of the things I had a love/hate relationship with was netsh’s use of context.  In netsh, you can set context via navigation and then you don’t need to provide that context on the command line.  For instance you navigate into the FIREWALL and then you perform a set of operations and you don’t need to say that you are working on the firewall because it picks it up from the context.

I thought I would experiment with this a little this morning.  I don’t know if this is useful or not but it shows a couple of interesting scripting techniques so I thought I would share it anyway.  This is PUSH-NOUN.PS1.  You can push a NOUN context and then you don’t need to specify that noun for the first command in a command sequence – you just specify the verb.  You type "?" for help,  "exit" to exit and "! cmd" to escape and execute a command directly.  The examples will make it more clear. 

First the code:

# Push-Noun.ps1
# Sets a context which allows you to work on a noun similar to the way NETSH does

while ($true)
    Write-Host "[$Noun]> " -NoNewLine
    $line = $host.UI.ReadLine().trim()
    switch ($line)
    "exit"   {return}
    "quit"   {return}
    "?"      {Get-Command "*-$Noun" |ft Verb,Definition -Auto |out-host}
                $Cmd = $_.SubString(1)
                Invoke-Expression $cmd |out-host
    default  {
                $Verb,$args = $Line.Split()
                $Cmd = "$verb-$Noun $args"
                Invoke-Expression $cmd |out-host

Now let’s run it:

PS> .\push-noun service
[service]> ?

Verb    Definition
—-    ———-
Get     Get-Service [[-Name] <String[]>] [-ComputerName <String[]>] [-Include <String
New     New-Service [-Name] <String> [-BinaryPathName] <String> [-DisplayName <String
Restart Restart-Service [-Name] <String[]> [-Force] [-PassThru] [-Include <String[]>]
Resume  Resume-Service [-Name] <String[]> [-PassThru] [-Include <String[]>] [-Exclude
Set     Set-Service [-Name] <String> [-DisplayName <String>] [-Description <String>]
Start   Start-Service [-Name] <String[]> [-PassThru] [-Include <String[]>] [-Exclude
Stop    Stop-Service [-Name] <String[]> [-Force] [-PassThru] [-Include <String[]>]
Suspend Suspend-Service [-Name] <String[]> [-PassThru] [-Include <String[]>]

[service]> get a*

Status   Name               DisplayName
——   —-               ———–
Running  AeLookupSvc        Application Experience
Running  ALG                Application Layer Gateway Service
Stopped  Appinfo            Application Information
Running  AppMgmt            Application Management
Running  AudioEndpointBu… Windows Audio Endpoint Builder
Running  Audiosrv           Windows Audio

[service]> get |where {$_.name -match "^A" -AND $_.Status -eq "stopped"}
Status   Name               DisplayName
——   —-               ———–
Stopped  Appinfo            Application Information

[service]> !gps *ss

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
——-  ——    —–      —– —–   ——     — ———–
    673       8     2308       6636    70     6.75    492 csrss
    539       7    16044      21460   190   149.98    540 csrss
   1633      16    11040      17680    68    67.47    656 lsass
    866       5     3976       5720    33     2.56    548 psxss
     33       1      312        760     5     0.08    428 smss

[service]> exit

Note that given the mechanisms we have, this only really works with the first command in the sequence.  Image that you wanted to do something like:

PS> Get | where {$_.name -match $test} |STOP

Where STOP did not have to specify the NOUN.  You really can’t do that today.  We have been thinking about exposing a TOKEN-NOT-RESOLVED event which would call a user-defined function which would allow you to do runtime fixups.  If we had that mechanism in place, you could do this.   Hmmmmmm.


Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx