Sunday, June 19, 2005 7:22 PM bart

Adventures in Monad - part 1

In the previous post I mentioned how you can obtain a copy of the MSH (Microsoft Shell) Beta 1, codenamed "Monad". In this (and the following) post(s) I assume you already have installed MSH and it's up and running on your machine. You can start it with MSH.exe or through the Start Menu which will contain a shortcut to the Microsoft Shell after installation.

Getting started

When you have started MSH you should see something like this:

Microsoft Command Shell
Copyright (C) 2005 Microsoft Corporation. All rights reserved.

MSH>

Some basic commands to kick off with are:

  • clear - to clear the screen
  • dir - for directory listings (same as get-childitem)
  • help ... - replace ... by some command name to show the "man"-pages

just to have something in your toolbox to play with :-).

Now, let's start by changing the command prompt using the Function Prompt command:

MSH> Function Prompt { "Bart> " }
Bart>

Hmm, rather weird syntax you might think. Well, maybe or maybe not. More will become clear as I explain the basic principles of the MSH shell environment. So, let's show you the usage of variables:

Bart> $prompt="Hello> "
Bart> Function Prompt { "$prompt" }
Hello>

Basically the $ symbol tells the MSH environment to "interpret" what follows. In the first line we define a variable, in the second line we use it. Now, let's change the variable:

Hello> $prompt=$prompt.Substring(0,5) + " World " + "> "
Hello World >

Any clue? Right, the $prompt variable is of the managed type System.String, therefore we can use any of the methods defined in the System.String class of the .NET Framework. By just calling $prompt, you'll show the contents of the variable:

Hello world > $prompt
Hello world >
Hello world >

So, the second line in this sample is nothing more than a print of the $prompt variable contents. Let's show one more action performed on the variable:

Hello world > $prompt.ToUpper()
HELLO WORLD >
Hello world >

Okay, this should be clear I guess. But there's more of course. We'll stay with the definition and usage of variables for now:

MSH> $i = 1
MSH> $i
1
MSH> $i++
MSH> $i
2
MSH> $i+=5
MSH> $i
7
MSH> $i=$i*$i
MSH> $i
49
MSH> ++$i
MSH> $i
50
MSH>

Operators such as +, -, *, /, +=, -=, *=, /=, ++, -- are all avaible in the MSH environment. In this sample, $i is an integer value. What about type conversions?

MSH> $j="2"
MSH> $j
2
MSH> $j++
 : The '++' operator only works on numbers not on 'System.String'
At line:1 char:4
+ $j++ <<<<
MSH>

Hmm, that's right isn't it? When you'd use the +-operator, you'd end up with a string concatenation. So, how can we tell MSH that $j should be converted to an integer?

MSH> $j=[int]$j
MSH> $j++
MSH> $j
3
MSH>

In order to declare a variable of a certain type, just do this:

MSH> $k=[long]1
MSH> $k
1
MSH>

What about arrays? Supported too:

MSH> $array=1,2,3,4,5
MSH> $array
1
2
3
4
5
MSH>

This is the ideal place to introduce the Write-Host (more or less the equivalent of echo in the classic Windows shell) "cmdlet" (more information about "cmdlets" follows in a minute):

MSH> Write-Host $array
1 2 3 4 5
MSH> Write-Host "The array contains $array"
The array contains 1 2 3 4 5
MSH>

Want to know more (e.g. about coloring the output)? Just execute "help Write-Host". Concerning variables, there are additional cmdlets that can be useful, such as Set-Variable. This allows to specify a variable to be read-only, static or private for example (more information via ... "help Set-Variable").

In fact, you can create any .NET type by using the [...] syntax. Well, that's what should be possible in the future. Today, you can only construct objects based on a string (e.g. a System.DateTime object based on a string representing the date). Thus, something like $g=[Guid] simply doesn't work (yet?).

Now, back to the array-stuff. Arrays have indexers, right? That's the case in MSH too:

MSH> $array[0]
1
MSH> $array[0..1]
1
2
MSH> $array[0,2]
1
3
MSH> $array[0..2]
1
2
3
MSH>

As the matter in fact, quite flexible indexers as you can see. Another symbol is @, which can be used to create associative arrays (read: hashtables):

MSH> $hash=@{ name="bart"; array=$array; location=$(Get-Location); someint=2}
MSH> $hash

Key                            Value
---                            -----
name                           bart
array                          {1, 2, 3, 4, 5}
location                       C:\Documents and Settings\Administrator
someint                        2


MSH>

Okay, please shoot me ... I was running as Administrator :$. Well, not much a problem this time as I was running in a sandboxed VPC but right, that shouldn't be much of an excuse. Now, as you can see, the hashtable seems to work:

MSH> $hash["name"]
bart
MSH> $hash["array"][1..3]
2
3
4
MSH>

You know, first rule of thumb ... arrays are pointers. So, what if we change the array, e.g. by reversing it (shows how to call Array.Reverse in Monad):

MSH> [Array]::Reverse($array)
MSH> $hash["array"][1..3]
4
3
2
MSH>

I guess you know now how to display the current date and time?

MSH> Write-Host [DateTime]::Now
[DateTime]::Now
MSH> Write-Host ([DateTime]::Now)
6/19/2005 6:09:42 PM
MSH> [DateTime]::Now

Sunday, June 19, 2005 6:09:53 PM


MSH>

Don't get tricked by the Write-Host cmdlet. You need to use braces in order to have some part of the argument interpreted. Now, let's turn to the real stuff with some program structures:

MSH> foreach ($i in $array) { $i }
5
4
3
2
1
MSH>

or with some manipulation stuff

MSH> $sum=0
MSH> $j=0
MSH> foreach ($i in $array) { $p=$i*$i; $array[$j++]=$p; $sum+=$p }
MSH> $sum
55
MSH> $array
25
16
9
4
1
MSH>

Similarly, there are if-elseif-else and switch statements and loops with while or for:

MSH> $i=0
MSH> while ($i -lt 10) { Write-Host ($i++) }
0
1
2
3
4
5
6
7
8
9
MSH>

Note you need to use -lt to perform lower than comparison. In an analogous fashion there is support for -gt, -le, -ge, -eq, -ne and even for more complex stuff such as -like (pattern matching), -match (regular expressions), -is (type checking as in C#), -contains (check for occurrence of a certain value in a "group", such as an array):

MSH> $b=$array -notcontains 4
MSH> $b
25
16
9
1
MSH>

As a side-note there is some other cool stuff around arrays, such as the use of tail-expressions (cf. Haskell):

MSH> $k,$l,$m=$array
MSH> $k
25
MSH> $l
16
MSH> $m
9
4
1
MSH>

Variable $m contains the tail of the list after assignment of the first two elements to $k and $l.

In order to create an endless loop, use while ($true):

MSH> while ($true) {} #press CTRL-C to stop
MSH>

We've seen the cmdlet Write-Host already. We always did specify the name explicitly, however you can call it indirectly too using the &-operator:

MSH> $cmd="Write-Host"
MSH> &$cmd "Hello"
Hello
MSH>

This is useful when you're creating scripts that need to be altered afterwards in a very broad way.

What is a CmdLet?

A CmdLet (or command-let) is the basic vehicle that drives Monad. Basically it's the equivalent of a "command" in other shells. So it's a basic unit of work. However, it has some "natural language" aspects. First there is a verb and a noun, seperated by a dash character:

verb-noun

The verb indicates what will happen: e.g. get something, add something, clear something, copy something, format something, create something, write something, and so on. The noun is the indicator of the real functionality, the "subject" of the action. Monad comes with a bunch of built-in Cmdlets. A little list with some samples:

  • Get-Datae, Get-Drive, Get-Host, Get-Help, Get-Variable, Get-Item
  • New-Item, New-Object, New-Variable
  • Read-Host, Write-Host
  • Remove-Variable
  • Rename-Item
  • Get-Process, Get-Service, Restart-Service

In a next episode of "adventures in Monad" I'll explain how these cmdlets work, how you can "pipe them together" and how to create your own cmdlets (h).

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Filed under:

Comments

No Comments