PowerShell

PowerShell
is Microsoft's replacement for the well-worn command prompt. It's pretty slick - definitely puts Windows on par with Unix in terms of shells. Unlike command.com or cmd.exe, it's an actual programming language - with first-class functions, no less. It's got all the usual functional programming language friends - though the names have all been changed ("map" is spelled "foreach-object", "filter" is spelled "where"). What distinguishes it from other scripting languages is the amount of effort that's been put into making Windows features easily available. PowerShell makes it easy to access .Net, COM, WMI, the registry, you name it. Examples:

Here are a couple of examples:

An MD5 command - using .Net libraries

function md5($cleartext)
{
    $bytes = (new-object System.Text.UTF8Encoding).GetBytes($cleartext)
    $hash = [System.Security.Cryptography.MD5]::Create().ComputeHash($bytes)
    [string]::Join("", ($hash | % {$_.ToString("x2")}))
}

which

Note that ? is an alias for "where" and % is an alias for "foreach-object"

set-alias exist test-path

#standard "which" command, but returns all matches, rather than only
#the first.
function which($p) 
{
    $dirs = ($env:Path).split(";") | ? { $_.length -gt 0 -and (exist $_)}
    $names = @($p) + $env:Pathext.split(";") | % {$p + $_}
    $dirs |% {$d = $_; $names | % { join-path $d $_} | `
        where {exist $_ } | % {(resolve-path $_).Path} }
}

Notice the extensive use of pipes. PowerShell pipes objects, not text. This is much more like functional programming than like UNIX shells. Let's look at a piping a range of numbers:

PS C:\> 1..5
1
2
3
4
5
PS C:\> 1..5 | foreach {$_ * 2}
2
4
6
8
10

All right so far? These are numbers being passed through the pipeline, not text. Here:

PS C:\> 1..5 | foreach {$_.gettype()}

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Int32                                    System.ValueType
True     True     Int32                                    System.ValueType
True     True     Int32                                    System.ValueType
True     True     Int32                                    System.ValueType
True     True     Int32                                    System.ValueType

What can you do with a number once you've got one?

PS C:\> 1 | get-member


   TypeName: System.Int32

Name        MemberType Definition
----        ---------- ----------
CompareTo   Method     System.Int32 CompareTo(Int32 value), System.Int32 Com...
Equals      Method     System.Boolean Equals(Object obj), System.Boolean Equ...
GetHashCode Method     System.Int32 GetHashCode()
GetType     Method     System.Type GetType()
GetTypeCode Method     System.TypeCode GetTypeCode()
ToString    Method     System.String ToString(), System.String ToString(IFor...

Not too much. How about static methods?

PS C:\> 1 | get-member -static


   TypeName: System.Int32

Name            MemberType Definition
----            ---------- ----------
Equals          Method     static System.Boolean Equals(Object objA, Object ...
Parse           Method     static System.Int32 Parse(String s), static Syste...
ReferenceEquals Method     static System.Boolean ReferenceEquals(Object objA...
TryParse        Method     static System.Boolean TryParse(String s, Int32& r...
MaxValue        Property   static System.Int32 MaxValue {get;}
MinValue        Property   static System.Int32 MinValue {get;}

We can also define functions that are pipeline-aware:

PS C:\> function sum() {
>> begin { $total = 0 }
>> process { $total += $_ }
>> end { $total }
>> }
>>
PS C:\> 1..5 | sum
15

Quite a step up from cmd.exe.