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
# WORKS WITH V1
param($noun)
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}
{$_.StartsWith("!")}
{
$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
PS>
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.
Enjoy!
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
0 comments