Getting started – customize your prompt
Sunday, 12 June 2005
Ok, so I’ve talked about how great the Monad language, and command-line interface
is. I’ve also said that it changes the way you think about the command line.
How so?
Well, let’s play with the shell for a bit. If you haven’t already downloaded it,
you can get it from BetaPlace:
1) Go to http://beta.microsoft.com
2) Log in using the guest ID, mshPDC
3) Follow the download directions
Launch the shell, and you’re greeted with the friendly prompt:
Friendly, but not as useful as it could be. Monad defines its prompt via the “prompt”
function. Let’s create one. Oh, and let’s make it have the current directory in
it. But how do we get that? One of the important commands to help you spelunk through
Monad is is called “get-command.” Monad commands are always in the form of [verb]-[noun].
You always perform an action (verb) on something (noun.) Now, back to our goal:
Searching by the verb didn’t help much, let’s try a noun:
Several items look promising. Specifically, get-location.
Drive Provider ProviderPath Path
—– ——– ———— —-
C System.Managemen… C:\Documents and… C:\Documents and…
That looks like what we want. Remember when I mentioned that Monad was object-based?
Well here’s an example of where we get to use that functionality. Here we go:
MSH>$location.Path
C:\Documents and Settings\Lee
Ahh, that’s exactly what we want. We’ve stored the results of get-location into
a variable, and then accessed its Path property. The two steps aren’t actually required,
so we’ll shorten them to:
C:\Documents and Settings\Lee
Here’s another fun example — find out how many ‘get-*’ commands Monad has. The
get-command call returns a list, so you can determine its size using the ‘Count’
property:
29
Perfect. Notice that we haven’t ever had to do things like “echo”, or use the command
“write-host.” This is because the commands we call generate objects. When your command
generates an object, it flows to the next stage in your command. This sequence of
commands that you specify (separated by a | symbol,) is called a pipeline. The output
of one becomes the input of the next. Since we haven’t specified any additional
stages, the Monad shell ultimately ouputs the final one to the default “sink” –
out-host. The out-host command tries its best to format its input suitable for display.
So, the implied command is:
C:\Documents and Settings\Lee
Now, back to our goal of customizing the prompt. As I mentioned earlier, you customize
your prompt by defining a function called “prompt.” How do I know? Try “get-command
*prompt*”
New MSH Prompt>
Your prompt function simply emits a string, which the MSH host knows to output.
But, we don’t want it to say “New MSH Prompt,” we want it to output the current
directory (err, location):
MSH C:\Documents and Settings\Lee>
Defining it this way will not persist between console sessions, though. To make
your changes permanent add the prompt function to your MSH profile, “profile.msh.”
The default profile is actually nice enough the define a helpful variable called
“$profile”:
Notepad pops up with your current profile. Investigate it a bit, to see what it
does. You don’t have to understand it all, but it does give you an idea of some
features that Monad supports. When you’re done investigating, add your prompt function
to the file, then save it. I’ll put mine as the first thing after the copyright
header:
# PARTICULAR PURPOSE
#
function prompt { “MSH ” + (get-location).Path + “>” }
# If you set the environmental variable DebugMSH to 1, all script executions
…
Now, close your shell, reopen it, and enjoy your new prompt. However, we can make
it even better.
Command-line interfaces have long supported a facility to let you interact with
your console history. A lot of Unix geeks like to have their prompt display their
current history line number. That’s for two reasons. First, it lets you invoke a
previous command by its history ID. Second, it somehow ties in with our freakish
obsession with uptime. It’ll look like this:
We’ll cover it next time, but see if you can beat me to it.
[Edit: Updated .Length to .Count, as .Count is the standard!]
[Edit: Monad has now been renamed to Windows PowerShell. This script or discussion may require slight adjustments before it applies directly to newer builds.]


Subscribe to this blog.
No. 1 — June 12th, 2005 at 7:24 pm
I went to the site, logined using passport, entered the guest Id, and it took me to this page:
http://beta.microsoft.com/source/BPProgInfo.asp?ProgID=1073000000&Page=default.htm
It says that I need to fill out a survey in order to get an email with login id & password, I filled it, and I still didn’t get the email.
What is wrong?
Also, the sentense:
******Version 2.0: An update of the Command Shell preview will be available on March 12th 2004******
Isn’t it time to update the page?
No. 2 — June 12th, 2005 at 8:57 pm
Sorry, Ayende. I really should have written:
3b) Time elapses, and you get an email with even juicier download instructions.
BetaPlace is set up in such a way that our beta coordinator needs to authorize the applications. He generally does this daily, so expect an email some time Monday.
As for the messaging on the beta site — you’re right, it is time to update the page! As I promised in my last post, one is coming shortly.
No. 3 — June 13th, 2005 at 3:24 pm
MSH>(get-command get-*).Length
No. 4 — June 13th, 2005 at 3:32 pm
MSH>(get-command get-*).Length
This works fine but the preferred method of doing this is:
MSH>(get-command get-*).Count
Why? It turns out that while .NET has a far more consistent API set than WIN32, it is not up to the consistency standard required by management applications and admins. This is one of the reasons we added AliasProperties and ScriptProperties to Monad. When Monad starts, it reads a set of XML files which provide additional metadata about the objects that are produced by .NET and cmdlets. Of of the things you can do with this metadata is to dynamically add additional properties to a type.
It turns out that some collection classes use COUNT and others use LENGTH. We don’t want admins to have to worry about what type of collection they have so we added COUNT as an AliasProperty to classes that used Length. Thus, you are always better off using COUNT instead of Length (count should always work – if it doesn’t – it means that we need to update the metadat, wereas length will only "sometimes" work).
We do the same thing for NAME. Lots of classes use things like PROCESSNAME and SERVICENAME. We alias those to NAME.
jps
No. 5 — June 19th, 2005 at 9:32 am
How can i emit a newline character?
i want something like
MSH: path
>
tia
dominick
No. 6 — June 19th, 2005 at 7:44 pm
Hi Dominick.
MSH uses the back-tick as its escape character. The back-tick is FAR less common than the back slash, meaning that you need to spend less time double-escaping:
MSH C:\Documents and Settings\Lee >"Hello`nWorld"
Hello
World
No. 7 — June 23rd, 2005 at 6:23 pm
This is really neat, I have done more shell scripting then any man should and this is truly pretty neat. Shell scripts are the most unmanageable collection of bits ever. I could also send you a list of things that would go into my perfect shell if you would like such a thing.
Overall, cool beans.
No. 8 — June 29th, 2005 at 5:20 am
$profile returns empty/null
The profile.msh file exists in my documents, and is in fact being used by Monad, but the variable you advertise doesn’t seem to be populated with anything.
No. 9 — June 29th, 2005 at 7:02 am
Collin: We would love to hear your input — as would the rest of our community. Would you be able to post your wish list to the BetaPlace newsgroup?
Keith: $profile is no longer populated by default, as of the most recent drop. I’ll be posting a blog entry soon (I have it in the queue) that resolves your issue :)
Lee