Saturday, June 12, 2004

With some things cleared up and some ground work done, here is one of the first the things I want to talk about in Monad – the new Microsoft Command Shell(msh).

 

In my first article I talked about ‘cmdlets’ and that they return .Net objects. Here is one other smart and subtle thing they did with Monad – Parameters to cmdlets come not only on the command line but can also come from objects in the input object pipeline.

 

Some basic stuff on cmdlets and parameters which might help set the stage for understanding things. A command-let can define fields/member variables as part of the command let class. These fields can then be decorated by attributes so that they can be treated as parameters to the commandlet.

 

In code it actually looks like this:

 

[CmdletDeclaration( "demo", "cmdlet" )]

public class HelloWorld : Cmdlet

{

        [ParsingPromptString("Enter a string to echo: " )]

        [ParsingParameterMapping(0)]

        [ParsingMandatoryParameter]

        [ParsingAllowPipelineInput]

        private string message;

(This snippet is a modified version of code from the beta documentation)

 

Now this might look a little over decorated with attributes, but the thing I want you to notice is that that is a member called ‘message’ which simply has a few attributes applied. Applying those attributes causes ‘message’ of type System.String to be a parameter required for our command let.

 

Unlike traditional command line exes, the responsibility of parsing the command line options and assigning them to internal variables is not the responsibility of the program but is automatically done for you by the Monad shell.

 

Which is to say that by the time code that you write in the cmdlet is executed, values are already assigned to the parameter variables by the shell. (Not strictly, but almost).

 

The shell can let you assign parameter values either by specifying them at a certain postion in the command line – for example argv[1] will be the value for ‘message’, argv[2] will be the value for something else and so on. Or the shell lets you  specify the parameter name and then the value

> demo-cmdlet –message “hello world” -< parameter name >  < value >

 

The other good thing (which is new and what this log entry was about) is that the value for a parameter can be extracted from an object on the pipeline if the object has a field/member of the same name. (The types have to be consistent)

 

So if I have a command let that generates an object of type foobar that has a member of name ‘message’, I can pipe the output to the commandlet that required a parameter of name ‘message’ and it would all work.

> create-foobar | demo-cmdlet

Would create foorbar instances that get piped to demo-cmdlet which uses the ‘foobar.message’ as its message parameter.

 

Where would this be used?

 

For an example there is a command-let called ‘get-process’. It lists the running processes on the system. To be more precise it returns a collection of process instances which get standard formatted to the console.

MSH 5 C:/>get-process

 

ProcessName                  Id   HandleCount   WorkingSet

-----------                  --   -----------   ----------

CcmExec                     288           480     14008320

cmd                         804            22      1421312

csrss                       464           669      4743168

dfssvc                      316            70      3260416

DWRCS                      1444            44      2560000

explorer                   3520           366     20926464

FrameworkService           1544           303      9367552

Idle                          0             0        16384

 

Similarly to see all the instances of notepad that are running I would simply say

MSH 16 C:/>get-process note*

 

ProcessName                  Id   HandleCount   WorkingSet

-----------                  --   -----------   ----------

notepad                    3912            16      1912832

notepad                    4044            16      1912832

notepad                    3056            16      1912832

 

Now there is a cmdlet called stop-process which can terminate a process if you pass it the process id as a parameter. The parameter name that stop-process expects is called ‘Id’.

MSH 11 C:/>command stop-process

 

Command: stop-process

Command Parameters:

        Id                    : Int32[]         : Optional

        ProcessName           : String[]        : Optional

 

So with all the earlier talk you can conclude that if I simply wanted to kill all the instances of notepad that are running I could type

MSH 11 C:/>get-process note* | stop-process

 

Now isn’t that clean? Just to add a bit of garnishing to that, Monad defines ‘ps’ as an alias to get-process and ‘kill’ as an alias to ‘stop-process’.

So now you can say

MSH 11 C:/>ps note* | kill

 

Cool? This works on Monad today.

 

Prev:

Introductory entry about Monad 

 

Next:

ObjShell: A precursor of Monad?