Why I Use Powershell

I’ve been using Powershell for just over a year now, and its effect on my development workflow has been steadily increasing. Looking back, I have no doubt that it is the most important tool in my belt – to be perfectly honest, I’d rather have Powershell than Visual Studio now. Of course, that’s not to say Visual Studio isn’t useful – it is – but rather more that Poweshell fills an important role in the development process that isn’t even approached by other tools on the platform. Visual Studio may be the best IDE on the market, but at the end of the day, there are other tools that can replace it, albeit imperfectly.

To take some shavings off of the top top of the iceberg that is why I use Powershell, I’d like to share a recent experience of Powershell delivering for me.

Setup for Failure

As a co-orgranizer for the New York .NET Meetup I was tasked with getting the list of attendees off of meetup.com and over to the lovely people at Microsoft who kindly give us space to meet. Now, you might think there’d be some nifty “export attendee list to CSV” function the meetup.com website, but like a lot of software, it doesn’t do half the things you think it should and this is one of those things. Usually my colleague David assembles the list, but this particular month he was out on vacation. He did, however, point me over to a GitHub repository of a tool that would do the extraction.

Following his advice, I grabbed the repository and brought up the C#/WinForms solution in Visual Studio. Looking at the project structure, I was a bit stunned at the scale of it all. The author had divided his concerns very well into UI, data access, the core infrastructure, and, of course, unit testing. I thought that was pretty peculiar considering all I wanted to do was get a CSV of names and such off of a website. Far be it for me to criticize another developer’s fastidiousness. Maybe it also launched space shuttles; you never know.

In what I can only now describe as naivety, I hit F5 and expected something to happen.

I was rewarded with 76 errors.

Right off the bat, I realized that the author had botched a commit and forgot a bunch of libraries. I was able to find NHibernate fairly easily with Nuget, but had no luck with “Rhino.Commons.NHibernate”. I tried to remove the dependency problem, but didn’t have much luck. And the whole time I was wondering why the hell you needed all these libraries to extract a damn CSV from the internet.

The Problem

Rather than throw more time after the problem, I decided to forge out on my own. Really, how hard could it be to

  1. Get an XML doc from the internet
  2. Extract the useful data
  3. Perform some heuristics on the names
  4. Dump a CSV file

Being a long-time C# programmer, my knee-jerk reaction was to build a solution in that technology. Forgoing a GUI to spend as little time as possible in building this, I’d probably build a single file design that ostensibly could consist of a single method. And if that were the case, why not script it?

So if I was going to write a script, what to use? I could write a JavaScript and run it on node.js, but it’s lacking proper CSV utilities and I’d have to run it on something other than my main Windows box. Not to mention I don’t particularly writing in JavaScript, so I’d probably write it in CoffeeScript and have to compile it, etc, etc.

I briefly considered writing an F# script, but I suspect only about ten people would know what on earth it was, and, at the end of the day, I would like to share my script to others.

The Solution

In the end, I concluded what I had known already: Powershell was the tool to use. It had excellent support for dealing with XML (via accelerators) and, as a real scripting language, had no pomp and circumstance.

Here’s the script I ended up writing:

function Get-MeetupRsvps([string]$eventId, [string]$apiKey) {
    $nameWord = "[\w-']{2,}"
    $regex = "^(?'first'$nameWord) ((\w\.?|($nameWord )+) )?(?'last'$nameWord)|(?'last'$nameWord), ?(?'first'$nameWord)( \w\.|( $nameWord)+)?$"

    function Get-AttendeeInfo {
        process {
            $matches = $null
            $answer = $_.answers.answers_item
        
            if(-not ($_.name -match $regex)) { $answer -match $regex | Out-Null }
            
            return New-Object PSObject -Property @{
                'FirstName' = $matches.first
                'LastName' = $matches.last
                'RSVPName' = $_.name
                'RSVPAnswer' = $answer
                'RSVPGuests' = $_.guests
            }
        }
    }

    $xml = [Xml](New-Object Net.WebClient).DownloadString("https://api.meetup.com/rsvps.xml?event_id=$eventId`&key=$apiKey")
    $xml.SelectNodes('/results/items/item[response="yes"]') `
    | Get-AttendeeInfo `
    | select FirstName, LastName, RSVPName, RSVPAnswer, RSVPGuests
}

To dump this to a CSV file is then really easy:

Get-MeetupRsvps -EventId 1234 -ApiKey 'MyApiKey' | Export-Csv -Path rsvps.csv -NoTypeInformation

And because of this design, it’s really extensible. Potentially, instead of exporting to a CSV, you could pipe the information into another processor that would remove anyone named “Thorsten.” Actually, that would look like this:

Get-MeetupRsvps -EventId 1234 -ApiKey 'MyApiKey' `
| ? { %_.FirstName -ne 'Thorsten' } `
| Export-Csv -Path rsvps.csv -NoTypeInformation

It’d be pretty difficult to do that if I’d written a C# executable – you’d have to go into Excel to do that. Or write a Powershell script. Just saying.

Here’s the real kicker: I spent all of five minutes writing my Powershell script and then spent minutes tweaking my regex to identify as many names as possible. I didn’t need to recompile, just F5 again in Poweshell ISE, which you have installed already if you’re on Windows 7. Since I left the Export-Csv part off during debugging, I could just read the console output and see what I got.

When I was happy with my output, it was dead simple to distribute: throw it in a GitHub Gist and move on with my life. If you decide to use it, all you need is Powershell installed (again, are you on Windows 7?) and the ability to copy and paste. No libraries. No worries. If you don’t like my regex, it couldn’t be easier to figure out how to replace it. If you want more fields, it’s easy to see where they should be added.

It’s really just that easy.

8 thoughts on “Why I Use Powershell”

  1. I think your point against F# holds just as well against PowerShell… the great majority of people can’t read those scripts.

    But I understand how you feel, I feel exactly the same about C# on LINQPad, and then anyone can later port the code into a VS project if they need it.

  2. I disagree. There’s definitely a few basics you need to know to read Powershell, but if the script writer keeps things clean, the complexity stays pretty low. Compared to F#, you’d have to understand the entire functional paradigm, type inference, and then all the syntax. The learning curve is most certainly steeper.

  3. @Daniel: you can write imperative code in F# just fine. No need for anyone to “understand the entire functional paradigm”. I’d even argue that there is no such thing as an “entire functional paradigm”… some ideas from functional languages can be used in most modern imperative languages, and in fact your PowerShell script does this.
    As for the syntax, I disagree that F# has a more complex syntax than PowerShell.

  4. I have been using Powershell lately and I do like it, but I feel it has quite a few quirks for such a new shell.

    Namely having a ps1 extension (why the 1?) and the way it mixes words and annotations when one needs to write scripts.

    I only use it as a better shell, for scripting I still resort to Python.

  5. Nice post Daniel, just don’t see why you would want to remove anyone named “Thorsten”, unless you want to use the resulting CSV file in order to send a mass mail announcing a meetup about building a SharePoint site.

  6. I’ve tried a few such scripts in powershell (also after considering F#), and I’ve got to say I’ve strongly regretted it. I should have used F#, and I’d recommend that to anybody else too. There are various issues.

    Firstly, the powershell syntax is a lot less friendly – it tries to be shell-like, but this often means that anything more complex than plain sequential commands turns into rather arcane commands, sometimes where spaces turn out to be signigicant. I’d much rather have to quote all strings that need to understand rules surrounding when I do and when I don’t, for instance; particularly for a language I use only occasionally.

    Secondly, powershell is unbelievably slow. For instance, piping a log file line-by-line through a regex seems to cause huge overhead; it’s *several* orders of magnitude slower than the equivalent bash grep script (and longer to boot). Sometimes this isn’t an issue, but I do want the *ability* to run my script on a large-isch amount of data.

    Thirdly, it doesn’t have an IDE to compare to Visual studio. It doesn’t have F#’s nice intellisense, typechecking, nor integration with pre-existing code.

    Fourthly, it’s got some disturbing bugs. E.g. http://connect.microsoft.com/PowerShell/feedback/details/572313/powershell-exe-can-hang-if-stdin-is-redirected (I mean, seriously, a *shell* that freezes when std-in is redirected? really?)

    Finally, I’d love the ability to start various processes asynchronously and wait until they’re all done. This is very easy in C#, fairly easy in F#, and not so easy in powershell. Lately I’ve tried some build automation and these tasks are slow enough that it’s nice to have some asynchrony to reduce the turn-around time.

    So all-in-all… powershell works, but there just aren’t that many strong reasons to pick it over F# (or even C#) whereas there are numerous areas where powershell fails badly.

Leave a Reply

Your email address will not be published. Required fields are marked *