Navigating Objects within the Microsoft Shell (MSH)

The new Microsoft Shell, MSH, gives easy unfettered access to your resources from the command line. This article will show how to navigate through your objects through the creation of providers in C#. On the scripting side, looping with foreach and controlling output format will be explained in detail. The results of a provider will be shown as plain text, HTML, and XML.

Golden Days of shell access

In the golden days (the ones we look at with rose-tinted glasses) of Microsoft's shell, results were just shown as stark white on black. Results were given back as raw text, in whatever layout was deemed appropriate for the block of data that was returned. As far as batch command execution, end users were forced to use a very basic scripting language — the BAT file. This provided limited flow control and was in general very weak for systems administration.

Shell Scripting with Ease

Fast forward to MSH. Microsoft has now provided a rich set of command tools and a much more dynamic environment in which to use them. Admins and powerusers alike may create scripting files with the MSH extension, using the "exec" command to run the scripts at the prompt (ie. exec FileStats.MSH). Users may create comments similar to the batch "rem" command by using the "#" symbol as the prefix to the comment, either at the beginning of a line, or at the end of a line of code. Even more powerful are the addition of variable scoping and user-defined functions, shown in the following sample:

function ScriptComplete
{
   if ($args.Length -gt 0)
   { $local:readkey = get/console $args[0] } 
   else { echo/console "Script Completed" }
}

... 

ScriptComplete "Script completed. Please press enter to exit."

This example shows some very interesting syntax uses. Functions are similar to those in Javascript, with variant input types. Only in MSH, they are not declared along with the function. Inputted variables are accessed through the "$args" array. Each function has an implicit array called "$args" which may or may not be null depending on how the function is called. The if statement uses the attribute "-gt" to represent "greater than"; there is also a corresponding "-lt" attribute to show "less than." Here, the if statement checks to make sure the input parameters are not null by checking the length of the argument array. If there is not a message sent into the function, a default one is displayed, and execution is given back to the command shell. If parameters are sent in, the "get/console" command reads an inputted value from the keyboard after displaying the message sent into the function, and stores it in an unused variable local to the function, $local:readkey.

This rudimentary example shows the value of flow control in a scripting environment. When users were using batch commands, they would use something like the "pause" command, which was an external executable file. Here, users may extend and customize functions, save this as "pause.msh" and whenever the user wanted to use it, they need only call "exec pause.msh." This is very usefull for complex scripts. Additionally, using the "call/command" command, users may pass values into a global "$args" array in the msh file that is called. So for the example provided, the last line could read as follows in order to provide a default message if there is no message sent from the calling script:

if ($args.Length -gt 0)
{ ScriptComplete $args[0] } else { ScriptComplete  }

Navigation and Providers

Providers are a way of allowing a user to navigate through data in a tree-like fashion. For example, you may have an Active Directory setup or a Registry that you wish to be able to navigate through at the command line. The most comfortable way to do this would be through the creation of a provider in MSH. This would allow the enduser to get to this information as a virtual drive in the command shell. In the attached example, a provider called "ENVAR" provides limited access to some environment information. For purposes of illustration, machine name and OS version were chosen.

[ProviderDeclaration("ENVAR", "Environment Vars", 
ProviderCapabilityFlags.None)]

To create a provider class, similar to declaring a commandlet, you must first declare the provider in an attribute. In this case, the term "ENVAR" was used, and this will be the drive name called in the MSH script. It also takes a description, and a number of additional provider capability flags. Our provider is inheriting from the NavigationCmdLetProvider, which allows users to use common file navigation techniques in order to navigate through the data which is exposed by the provider.

There are a great number of methods which can be overridden in order to increase the functionality of providers. Some example of these are RenameItem, NewItem, RemoveItem, and CopyItem, but for the purposes of this sample, we will stick to the minimum necessary to get the data available.

The first method shown to start up the provider is InitializeDefaultDrives(). This creates the drive or drives made available by the provider. For most of the methods a string, "path," is passed in so that the methods can only return the data that is valid in the context of the current path. For example, GetChildItems has a path passed in, and based on whether it sees the root path name "ROOT_PATH" or strings that refer to the child data ("OS" or "Machine"), it will return only the strings referring to the appropriate data.

The GetItem method is what actually writes out the data that the class will show to the end user when they show a directory of the current path. In this example, it is simply checking for an index of key words in the path string, and if found, returns the appropriate data. The path names do not actually have to exist, much like the way the WinFS file system will work. This provider has known metadata containing the machine name and OS version, and the user can view the path "ENVAR:\Machine\OS" or "ENVAR:\OS\Machine" to get at the same data.

Output Formatting

MSH also provides an easy way of formatting the output of the data returned by a commandlet. Microsoft has provided HTML output that automatically starts in the associated browser on the current machine. For example, if the user wished to display a directory of the current path in HTML, they would simply need to type the command:

get/children | out/html

The true richness of this scripting environment comes into play with other formatting output choices, such as a grid that is much like excel using the piped command "out/grid" or the filesystem through out/file. By combining features such as providers and looping, and the ability to output to many common formats (and having the ability to create your own custom formats through piped commandlets), MSH will remove many of the barriers discovered by Windows admins in the past.

In future articles, we will show how to deal with input from other commandlets as well as showing output in other formats and some more creative scripting.

Source Code

Download source code: MSH02.zip - 4kb

Reposted from Developer.com with permission



Comments

  • .MSH is missing something....

    Posted by Legacy on 12/30/2003 12:00am

    Originally posted by: David Little

    I'm not sure how to execute this little example. I don't have "exec" as a command on my XP box, and WScript and CScript don't recognize .MSH as a script type (actually, nothing recognizes .MSH).

    What am I missing something?

    - David

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds