Wednesday, May 16, 2007

Customizing your Shell Prompt

I do a lot of work from the command line. This involves both Linux and Windows. The BASH shell is very good and much better than the Windows cmd.exe. Microsoft has finally developed a much better shell called PowerShell (which will be the subject of a future post).

In both shells I find the prompt lacking a bit. The BASH shell only shows you the name of the lowest level directory you are in (i.e. if you are in /work/stuff/more/stuff it only shows "stuff"). The default windows prompts show you the entire path, but if the path is really long then you have only a couple of characters on the line before the command you are typing wraps around. Here is how to fix this problem with both the BASH shell and PowerShell...

I want a prompt that looks like this:
user@machine | /work/stuff/more/stuff
:: Type Command Here

This gives me the full path and I still have a full line to type my command. Also when looking back at previous commands I can see what directory they were executed in.

BASH
For the BASH shell the prompt text can be controlled by the PS1 envrionment variable. For a very good overview see this webpage or this webpage. To accomplish the above I have set my PS1 variable in the .bash_profile file to:

PS1="\e[33;1m\u@\\h | \e[34;1m\w \n\e[37;1m::"

This gives the user name in yellow, the path in blue, and the command text in yellow.

PowerShell
For PowerShell the prompt text is controlled by creating a function in your PowerShell profile file called prompt. The profile file does not exist by default, but PowerShell checks for it on start up. This file should be created as :
C:\Documents and Settings\\My Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1

Here is my prompt function:
########################################################
# Prompt
function prompt {
$d = pwd
$u = get-item Env:\USERNAME
$u = $u.value
$c = get-item Env:\COMPUTERNAME
$c = $c.value
$s = "$u@$c | "
$color = "Green"
write-host $s -ForegroundColor "Yellow" -NoNewLine
write-host $d -ForegroundColor "Green"
$promptText = "::";
$title = (get-location).Path;

write-host $promptText -NoNewLine -ForegroundColor "Yellow"
$host.UI.RawUI.WindowTitle = $title;

return " "
}
########################################################

This gives the user name in yellow and the path in green (the default PowerShell background is blue). The command text still ends up being white.

Tuesday, May 8, 2007

PowerShell

When I first started work after college I despised command line programs. The command line programs I had been exposed to in college were tedious to run and we never needed to run them lot's of time so automation was never an issue.

After a while I began to see the benefits of automation by the command line. The Windows command prompt leaves quite a bit to be desired. I always felt much more comfortable on a linux command line (and I admittedly don't know nearly as much as I would like about it) than a cmd prompt. The BASH shell is simply superior to the Windows command prompt.


Enter Windows PowerShell...
Microsoft (in usual fashion) finally implemented some bits of technology that had been employed by others for years, but did it with a twist. In most current shells data is passed between commands as text. Powershell actually passes .NET objects (classes). This gives the user some interesting and powerful options. For example...

$a = dir *.txt -recurse

will find all of the text files in the current directory and subdirectories. $a is actually an array of System.IO.FileInfo objects. You can access the first item in the array as $a[0]. To get the name of the fie $a[0].Name. To get the directory: $a[0].Directory.

A True Scripting Language
With PowerShell Microsoft made sure that it supported a true scripting language. It is similar in syntax to C# in many regards. A script or function can be called with arguments. There are several ways to get at these arguments. The most basic way is through the args array. This is an array of all arguments passed to the script (assuming you haven't explicitly defined what they should be).

There is a foreach-object loop that is quite useful. If I wanted to execute all of the batch files in all directories below the one I am in the following would work quite nicely:

:: dir -filter *.bat -recurse | foreach-object { cmd /c $_}

The first part of the command finds all of the batch files in this directory and all child directories. The pipe | says to pass the output from the dir command to the foreach-object command. The foreach-object takes a code block enclosed by {} as an argument. Each object in the array that is output from the dir command is accessed through the $_ variable. So the foreach-object command runs "cmd /c " for each batch file found. PowerShell cannot natively execute batch files, so the cmd command is need to execute the batch files.

Script Editing
As described in my first blog entry the I use the PSPad editor for writing PowerShell scripts. There are several other alternatives such as PowerShell Analyzer and PowerShell IDE.

Resources