Saturday, 30 March 2013

Creating a Mercurial-aware Powershell command line

This article will describe how we can create a Mercurial-aware Powershell command line. Mercurial is a (distributed) source control. It supports branch per feature and multiple other nice features that gives powerful control of the source code. To get Mercurial, check out the following url: Mercurial The Mercurial-aware Powershell command line will detect when the user enters a Mercurial repository folder on disk. This will be done via the hg branch command, which will return null if the user is not in a Mercurial folder and the name of the branch if the user is inside a Mercurial repository. Let's look at the needed code first, I put this code in my $profile file, use the cmdlet inside Powershell to open the $profile file: Invoke-Item $profile This will edit the profile file of your Powershell by launching Notepad. The $profile file is not perhaps created yet. If that is the case, enter the following command: new-item -ItemType file -path $profile Now retry the command Invoke-Item $profile if this command failed. If you just type: $profile at the Powershell prompt, you will get the file location of the profile file. Make note that Powershell ISE and Powershell prompt has different profile files.. Before I can show you the script, I must get the script transformed into readable colored HTML right? I type Import-Module PowershellPack, now I can use the nice cmdlet Copy-ColoredHTML. This copies the script as colored HTML into the clipboard. I can then paste the
function get-hgStatus(            
    $status = @('M','A','R','C','!','?','I')            
){            
    hg status --all |            
        where { $_ -match "^\s*[$Status]" } |            
        foreach { $_ -replace "\s+", ',' } |            
        ConvertFrom-Csv -Header Status, Path             
            
}            
            
function prompt {            
             
 $branch = (hg branch);            
 $status = get-hgStatus | Group-Object Status;            
            
 $modified = $status | where { $_.name -eq 'M' } | select -expand count;             
 $added = $status | where { $_.name -eq 'A' } | select -expand count;             
 $removed = $status | where { $_.name -eq 'R' } | select -expand count;             
 $untracked = $status | where { $_.name -eq '?' } | select -expand count;             
 $missing = $status | where { $_.name -eq '!' } | select -expand count;             
            
 Write-Host "PS $pwd" -NoNewline            
            
 if ($branch){            
              
  write-host "[" -NoNewline            
  write-host " $branch " -ForegroundColor White -NoNewline            
              
  if ($added){            
    write-host " " -NoNewline            
    write-host "+$added" -ForegroundColor green -NoNewline;            
  }            
            
  if ($modified){            
    write-host " " -NoNewline            
    write-host $modified -ForegroundColor yellow -NoNewline;            
  }            
            
  if ($removed){            
    write-host " " -NoNewline            
    write-host "-$removed" -ForegroundColor magenta -NoNewline;            
  }            
            
  if ($missing){            
    write-host " " -NoNewline            
    write-host "!$missing" -ForegroundColor red -NoNewline;            
  }            
            
  if ($untracked){            
    write-host " " -NoNewline            
    write-host "?$untracked" -ForegroundColor gray -NoNewline;            
  }               
            
  write-host "]" -NoNewline;            
            
 }            
            
 "> "            
            
}            
Note the use of a default value in the passed in $status variable, which is default set to an array of all the possible hg status flags. The hg-GetStatus function can be called individually like hg-GetStatus A to show all the added files in the repo. The script above uses the hg status command to get the status of the current hg repository in the folder. If the current folder is not a hg repository, a default prompt is shown instead. The output of the command hg status is converted into a structured object and then grouped by status using the Group-Object cmdlet. A pipeline is used to prepare the output of the hg status before it is transformed into the structured object. The count of each group is retrieved with the Select cmdlet, and using the -extract flag. The group counts are then shown to the user in the prompt and formatted with color. Make note of the use of -NoNewLine and -ForegroundColor. In addition, the Powershell function prompt will control how your prompt in the shell will look like, i.e. the command line. I put this prompt in the $profile file such that this is inited each time.

This is just an introduction to customizing the Powershell command line to a more suited hg aware prompt for developers using hg. There is actually already a better package available on the Internet for displaying the state of the hg repository in the folder displayed in Powershell, which is called hg posh. Check out hg posh on the following url:
hg posh


However, although hg posh is more correct, I like the look of this command prompt better ... For many hg users, this will suffice.. And here is our nice new Mercurial aware command line:

Friday, 29 March 2013

Hashtables in Powershell

Hashtables are good for creating dictionary-like data structures in Powershell, for look-ups and related functionality. The following powershell script code shows how to create a Hashtable in Powershell and to use and modify this datastructure:
$countries = @{            
    "Norway" = "Oslo";            
    "Denmark" = "Copenhagen";            
    "Sweden" = "Stockholm";            
    "Germany" = "Berlin";            
    "Italy" = "Rome";            
    "Burkina Faso" = "Ougadougou";            
};             
            
            
$countries.'Burkina Faso'            
$countries.ContainsKey('Norway')            
$countries.ContainsValue('Ougadougou')             
$countries.ContainsValue('London')            
$countries['Sweden']             
$countries.Add('France', 'Paris')             
$countries.France             
            
$countries.Remove('Burkina Faso')             
            
$countries.ContainsKey('Burkina Faso')
The code above creates a hashtable using the @{ and } literals, where each key and value is defined by key = value followed with a semicolon, note that this is optional and can be omitted. Hashtables are very user-friendly in Powershell. One can address a value in the hashtable by passing in the key in square brackets, like an index. It is also possible to perform manipulation of the hashtable, using Remove and Add. For the Add method, one supplies the key and value pair to add. For the Remove method, one supplies to the key to look for and then remove (this will of course also remove the value of the key-value pair).

It is also possible to query the hashtable using ContainsKey and ContainsValue methods, looking for keys or values. Note also that members of the hash table is available and shown in Intellisense using the Powershell ISE (Integrated Shell Environment). It is possible to say $countries.France, or $countries.'Burkina Faso'. The last hash table key address is encapsulated in quotes, since there is a space in the key. When calling GetType() on the hashtable instance $countries, the name Hashtable is shown.

To list up the keys and the values of a hashtable, use the Keys or Values member of the hashtable. Example:
$countries.Keys            
$countries.Values

Keyboard input in Powershell

Keyboard input in Powershell

Asking users for input in Powershell is very straight-forward. Use the Read-Host cmdlet in Powershell and add a string value to the -Prompt switch of the cmdlet to specify which text to display to the user. Assign the result from Read-Host to a Powershell variable, e.g. $myvariable. This variable can then be treated logically and control the further program execution and logical flow of the program.

The following code shows how this can be done:

            
function Ask-FavoriteColor ([int] $personsToAsk){            
<#
    .SYNOPSIS
    Ask about the favorite color
    .DESCRIPTION
    The favorite color was asked in the movie "Holy Grail"
    .PARAMETER personsToAsk
    The number of persons to ask
#>            
 foreach ($i in 1..$personsToAsk){            
            
  [string] $color = Read-Host -Prompt "What is your favorite color?"            
            
  if ($color.ToLower() -ne "blue"){            
     Write-Host "You didn't say blue!"             
  }            
  else {            
     Write-Host "Good choice!"             
  }            
 }            
            
}            
            
Ask-FavoriteColor 2


If you see the text at the top of the function, you will see how one can document the function using meta keywords such as .SYNOPSIS and .DESCRIPTION. When using the Powershell cmdlet Get-Help, e.g. Get-Help Ask-FavoriteColor -detailed, one will see the (detailed) documentation in the Powershell help page. This is rather equivalent to the obiqutous man command seen in Unix-based systems.

When creating Powershell functions, your functions should be documented using this technique. There are lots of other meta keywords that can be used. See this overview for meta keywords for documentation.

PS C:\Users\Tore Aurstad\Documents\Powershell Scripts\files> C:\Users\Tore Aurstad\Documents\Powershell Scripts\Input\KeyboardInput.ps1
What is your favorite color?: red
You didn't say blue!
What is your favorite color?: blue
Good choice!

'
PS C:\Users\Tore Aurstad\Documents\Powershell Scripts\files> Get-Help Ask-FavoriteColor -Detailed NAME Ask-FavoriteColor SYNOPSIS Ask about the favorite color SYNTAX Ask-FavoriteColor [[-personsToAsk] ] [] DESCRIPTION The favorite color was asked in the movie "Holy Grail" PARAMETERS -personsToAsk The number of persons to ask This cmdlet supports the common parameters: Verbose, Debug, ErrorAction, ErrorVariable, WarningAction, WarningVariable, OutBuffer and OutVariable. For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). REMARKS To see the examples, type: "get-help Ask-FavoriteColor -examples". For more information, type: "get-help Ask-FavoriteColor -detailed". For technical information, type: "get-help Ask-FavoriteColor -full".