What I’ve learnt about Powershell this week

Much to my suprise, Powershell holds up as a decent, testable language, with a few idiosyncrasies.

So far, I’ve been able to refactor Powershell to make use of its pervasive stream handling, and there are some useful tricks that can make Powershell look like other functional-style languages you know and love ( ruby, scala, LINQ, etc).

  • There are some nifty aliases:
     ? { test } |
    % { process; result } |
    % { process_result; finish}

    ? is an alias for ‘where-object’
    % in an alias of for-each.

  • More syntactic strangeness
     @{}

    creates an empty hashmap, but

    @[]

    wraps things into a list context. You’ll notice that when the following expression doesn’t work like you’d expect:

    (gci -path $path -filter "*.exe").Count

    this does:

    @[(gci -path $path -filter "*.exe")].Count
  • There is no built-in inject/foldl/aggregate function:
    function Fold-Left
    {
      [cmdletbinding()]
      param (
        [parameter(mandatory, position = 1)]$inital,
        [parameter(mandatory, position = 2)][scriptblock]$action 
      )
      begin { $sum = $inital}
      process {$sum = & $action $sum}
      end { write-output $sum }
    }

    We can use it to reduce intermediate variables. This will return a map containing symbol counts:

    $counts = a,a,a,a,b,c,d,e | 
       Fold-Left @{} { param($counts) $counts.$_ = $counts.$_ + 1}
    
      #$counts @{ "a" => 4; "b" => 1; .... }
  • For-each collects the return values of the for-each block.
    (1,2,3 | % { $_ + 1}) -eq @[2,3,4]

    However, if you want to modify items in place, it can be error-prone to remember the return value:

    @{"ting" = "hi"}, @{"thing" = "lo"} |
    % { $_.tong = $_.ting + "there"; $_} #note the return value
  • Tap is a useful function, here it is
    function Tap
    {
      [cmdletbinding()] 
      param([parameter(position=1)][scriptblock]$action)
      process {&action; write-output $_}
    }
    
    @{"ting" = "hi"}, @{"thing" = "lo"} | Tap { $_.tong = $_.ting + "there"} #note the return value is now ignored.

    This is a kestrel, for introducing side-effects. If your style is towards immutability you might want to use it differently.
    For instance, it’s great for logging:

    "command1", "command2" | Tap { Write-Host $_ } | Invoke-Expression $_

    And for turning it off, use…

    function Dont() {
      [cmdletbinding()] 
      param([parameter(position=1)][scriptblock]$action)
      process {write-output $_}
    }
    
    "command1", "command2" | Dont { Write-Host $_ } | Invoke-Expression $_
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: