A Quick Tour of Monad

You?ve got Monad, and now you?re wondering how the heck it works.  You?re wondering this because you haven?t read the first few sections of the GettingStarted.rtf that comes with it.  If you have it, go read it now, it answers all the basic questions.

(short pause while you read that) 

Okay welcome back.  So now you know that Monad is a fresh approach to providing a command line interface to an operating system.  Cmdlets (a Cmdlet is a small unit of shell functionality, but you knew that) produce and consume objects, and the command-line interface lets you do things like get objects, select properties from objects, and pipe objects around.

If you didn?t read it, maybe because you don?t have Monad yet, I?ll try to quickly mention the points that it covers, but not in nearly as much detail.

For example:

            dir

A simple command.  But it?s actually an alias for:

            get-childitem

What get-childitem does is enumerates the children of the current directory, producing an array for each item found.  The object it produces is a System.Info.FileInfo object. 

The FileInfo object part of the.NET framework?s base class library.  This is very powerful because it means that instead of some custom object created by the get-childitem command, you?ve got a real object that other functions in the framework understand, and that?s well documented (here) and understood by developers.

I said that get-childitem produces an array.  It?s actually a System.Array.  Again, using a class library object instead of defining a special kind of shell array.

So get-childitem produced an array and gave it back to the shell.  The default thing that the shell does with an object, when you haven?t piped it to another command or redirected it, is sends it to the format-table or format-list Cmdlet, which formats and writes objects to the console.  Format-list is used if one object is returned; format-table is used if many.

When you do a Dir, the first few lines look like this:

MSH> dir
   
Directory: FileSystem::D:\MSH

Mode    LastWriteTime            Length Name
—-    ————-            —— —-
-a—   Jun 06 17:25               2086 SomeFile.txt
-a—   Jun 06 17:25               3049 OtherFile.txt

There are some differences here from what you?d expect if this was simply a dump of FileInfo objects:

  • There?s a header, the ?Directory:? line.
  • Not every property of the FileInfo object is printed, just the Mode, LastWriteTime, Length, and Name.

How does the shell format the array of FileInfo objects that get-childitem returned?

The question gets even more interesting if you try writing out the objects in the array using syntax like this:

MSH> $f = dir

MSH> foreach ($fi in $f) { write-object $fi }

    Directory: FileSystem::D:\MSH 

Mode    LastWriteTime            Length Name
—-    ————-            —— —-
-a—   Jun 06 17:25               2086 SomeFile.txt
-a—   Jun 06 17:25               3049 OtherFile.txt

 In this case, we?re not just passing the results of Dir somewhere; we?re explicitly enumerating through the results of the Dir command and writing each object separately.

The answer is that there?s a file called FileSystem.Format.mshxml that describes how to format the FileInfo and DirectoryInfo objects that get-childitem returns. 

The format cmdlets looks at these files to determine how to format the object, including which properties to display and what template to use for the table, list and wide ?views? of the object.

There really is no built-in functionality in MSH to ?get a directory listing?.  The functionality of ?dir? is provided by a number of pieces of the shell, and the great part is most of those pieces will be the same for other commands. 

With most shells, every command has its own syntax.  If you want to get a list of files bigger than 10k in size, you have to figure out the syntax for the ?dir? or ?ls? command to do that.  If you wanted a list of processes with more than 10 threads, or a list of Event Log entries generated by a particular service or, well, anything, selected by any property, there?s only one syntax to figure out, and every command will support it.

XML

I wish there was more support for XML built into the shell, but you can still do some interesting stuff with what?s provided, and with the System.Xml namespace that the framework provides.

For example:

            $x = (new-object System.Xml.XmlDocument)
            $x.Load(?c:\iTunes.xml?)
            $keys = $x.SelectNodes(?/plist/dict/key?)
            $keys | foreach-object { $_ }

This prints the nodes that match the XPath query ?/plist/dict/key? in the XML file loaded, using no built-in shell XML functionality; it all comes form the .NET framework.

Developers familiar with the .NET framework have a huge advantage when it comes to using MSH.  The shell isn?t so much a provider of functionality as it is a way to access functionality provided by the framework. 

I think this is a good thing.  The more ways there are to access the framework, the more familiar everyone becomes with it.  It makes it easier to really choose the best tool for the job, be it VB, C#, C++, or a shell script.  The knowledge of how the system works and what functionality it provides is portable.