A Get-Random Gotcha #PowerShell - How I Was Robbed :-)
OK, so last night at the Inaugural PowerShell Users Group Meeting (@CharlottePoShUG) there were some giveaways. Naturally our excellent host, Jim Christopher, got everyone's name and assigned each participant a number. If you use PowerShell then you know the next step is naturally Get-Random for selecting the winner from the 29 attendees.
There were 4 prizes to give away so the following one-liner was put into the shell to generate those winners.
1..4 | Get-Random -Maximum 29
What we essentially see here is that the range operator calls Get-Random 4 times with a maximum number of 29. This looks perfect since there were 29 attendees. There turned out to be 5 or 6 prizes and Get-Random -Maximum 29 was used for those calls as well with the following numbers chosen out of some of those calls [14, 15, 17]. Why is this important? Because I was 16. Truly it's not important but I realized something pretty quickly…I got skipped incorrectly and one guy didn't even have a shot!
Understanding the Problem
Here's the fun thing about Get-Random. If you pass it a list or collection of items it will randomly select one of the items from the list as I do with Get-vBeers and Test-vMotion. You can include some criteria if need be but it will grab any one of them. If you just run Get-Random on its own however the behavior is a bit different. Here's some text from Get-Help:
The Get-Random cmdlet gets a randomly selected number. If you submit a collection of objects to Get-Random, it gets one or more randomly selected objects from the collection.
Without parameters or input, a Get-Random command returns a randomly selected 32-bit unsigned integer between 0 (zero) and Int32.MaxValue (0x7FFFFFFF, 2,147,483,647).
You can use the parameters of Get-Random to specify a seed number, minimum and maximum values, and the number of objects returned from a submitted collection.
You see what happened there? No? No worries, the first time I used Get-Random I had missed it as well.
When you run Get-Random without any collection it will choose a random 32-bit unsigned integer starting with ZERO. When you run Get-Random with a set -Maximum value it will select a value up to, but not equal to, that value. See it now?
Essentially since Ed used -Maximum 29 we were working on a selection range of zero to 28. He did it absolutely perfect though. He set the script to grab 4 random numbers out of 29 possibilities. Unfortunately while we had 29 possibilities they were numbered 0-28 and not 1-29 like Jim had numbered. Honestly...would you have started numbering at 0? Nope. Me neither.
Again, this truly isn't a big deal for me but it brings up an important issue that sometimes comes up with your scripting. You really need to make sure that you understand the default behavior of the cmdlets you use and ensure that it matches precisely with the results you're expecting. Those expectations are often driven by your customer (often yourself) but you have to make sure you know that the cmdlet is going to give you the output you require based on your constraints. I am certainly not pointing fingers at either of these guys but this is a simple example of how the customer (Jim) and the developer (Ed) both did their jobs but weren't necessarily in sync on the required results. While this isn't a big deal at the users group meeting it could have serious impacts in your expected results of your sensitive environments.
How could this had been done differently? I'll approach it two ways, neither of which is necessarily more correct than the other.
Jim could have started his count at zero to match his list to the default output of the script that Ed created.
Ed could have set a minimum of 1 and a maximum of 30 to ensure that the numbers selected from matched precisely to Jim's list.
1..4 | Get-Random -Minimum 1 -Maximum 30
Had they done option 1 then I would have been one of the randomly selected winners and the guy to my left and right would have been out of luck instead of being winners. Additionally the last guy that signed in would have had a shot of having his number called.
In conclusion make sure you understand the behavior of the cmdlets you're using. Ed knows what he's doing and so does Jim. These guys are very talented scripting professionals which highlights that this can happen anywhere. Make sure you use Get-Help if you're unclear on how a cmdlet is supposed to work and grab one of Ed's books and get trained up in some serious PowerShell.
Huge thanks again to Jim and Ed for their time with the Charlotter PowerShell User Group. If you are in the area I highly recommend that you attend one of the meetings. You can check out the inaugural meeting recap at http://www.vtesseract.com/post/15437493633/inaugural-charlotte-powershell-users-group-meeting and hopefully there will be a recording posted so you can catch Ed's presentation.
May the -force be with you!
http://www.beefycode.com/
http://blogs.technet.com/b/heyscriptingguy/
https://twitter.com/#!/CharlottePoShUG