Use a Simple PowerShell Technique to Create Random Numbers

Use a Simple PowerShell Technique to Create Random Numbers

  • Comments 1
  • Likes

Summary: Microsoft Scripting Guy, Ed Wilson, teaches you how to use a simple Windows PowerShell cmdlet to create random numbers.

Microsoft Scripting Guy, Ed Wilson, is here. One of the questions I received when I was speaking at the SQL Rally in Orlando, Florida was whether I prefer working in the Windows PowerShell console, or do I prefer to use the Windows PowerShell ISE and write a script.

The answer, for me, is both. On any given day, I will start Windows PowerShell more than a dozen times. How do I know this? Well, I use Windows PowerShell to tell me. The following command queries the Windows PowerShell log for instances of event 400 that have been generated since April 1, 2011. Event 400 is generated each time Windows PowerShell starts up.

To get the Windows PowerShell event log, I use the Get-EventLog Windows PowerShell cmdlet. Because I do not like typing, I use wild cards to specify the PowerShell log. The results of the query return every event in the event log. I pipe that to the Where-Object cmdlet to filter everything except the event ID 400 and dates after April 1, 2011. The Measure-Object cmdlet counts the number of events that make it through the filter.

Get-EventLog -LogName *power* |

Where-Object { $_.instanceID -eq 400 -AND $_.timewritten -gt "4/1/2011"} |

measure-object

The Windows PowerShell event 400 is generated when the Windows PowerShell engine transitions to running. An example of this event is shown here.

Image of event

Anyway, during the 2011 Scripting Games, I needed to generate 10 random numbers that began with the number 2 and were a maximum of 20. This can be accomplished by using the following command line:

1..10 | % {Get-Random -Minimum 2 -Maximum 20 }

In the command, I create a range of numbers, 1 through 10. I pipe them to the Foreach-Object cmdlet (% is an alias). Inside the Foreach-Object cmdlet, I process the Get-Random cmdlet and use the minimum of 2 and the maximum of 20. The command and its associated output are shown here:

PS C:\> 1..10 | % {Get-Random -Minimum 2 -Maximum 20 }

18

2

3

9

19

9

13

10

4

8

18

2

3

9

19

9

13

10

4

8

The problem with this output is that there are duplicate numbers in the output. One thing that can be done is to pipe the output to the Get-Unique cmdlet. The problem with this is that the output must be sorted. To sort the output, I can use the Sort-Object cmdlet. The cool thing is that the Sort-Object cmdlet has a Unique parameter, which makes using Get-Unique redundant. The revised command is shown here:

PS C:\> 1..10 | % {Get-Random -Minimum 2 -Maximum 20 } | Sort -Unique

3

5

6

10

12

16

17

18

19

Now I have another problem. I do not have 10 random numbers. I only have 9 because a duplicate number was removed from the output.

I might be able to squirrel around and figure something out, but at this point, I generally give up and write a script. I will copy the commands from my command history into the Windows PowerShell ISE so I at least have something with which to begin working.

The first thing I did was create an empty hash table. I gave it the rather unimaginative name of $hash. Next I used a Do … While loop to allow me to loop through the random numbers. I used my code snippet tool to insert the Do … While loop because I do not write Do … While loops all that often when writing Windows PowerShell code. The empty hash table, Do statement and opening curly bracket are shown here.

$Hash = @{}

Do

  {

I used the same Get-Random command, but this time I assign it to the $a variable. Because I want a collection of random numbers that are unique, I needed a way to get a specific number of unique numbers. To do this, I add the number to both the key and the value of the hash table. This generates an error if the key previously has been used (the non-unique random number case); and therefore, I use Try / Catch. I am not interested in really doing anything when I catch the error, so I supply empty curly brackets. This portion of the script is shown here:

$a = Get-Random -Minimum 2 -Maximum 20

Try { $hash.add($a,$a) } Catch {}

Now I get rid of the number that is stored in the $a variable, and I add my while condition. I will keep looping around while the count contained in the hash table is less than or equal to 9. This is because the condition will always be one less than my desired number. The last thing I do is display the collection of keys from the hash table, as shown here:

   $a = $null

  } While ( $hash.count -le 9 )

  $hash.Keys

The complete Get-UniqueRandomNumbers.ps1 script appears here.

Get-UniqueRandomNumbers.ps1

$Hash = @{}

Do

  {

   $a = Get-Random -Minimum 2 -Maximum 20

    Try { $hash.add($a,$a) } Catch {}

   $a = $null

  } While ( $hash.count -le 9 )

  $hash.Keys

Interestingly enough, there is a count property that I can use to specify a specific number of objects to return from a collection. By using this command set, I can simplify my previous script down to a single command. This one line command is shown here:

PS C:\> Get-Random -Count 10 -InputObject (2..20)

15

9

6

2

17

19

10

18

14

3

Well, that is about it. As you can see, when I am working in the Windows PowerShell console, I often migrate to the Windows PowerShell ISE, and then vice versa. There are two things I really like about the Windows PowerShell console—it loads really fast, much faster than the Windows PowerShell ISE. And it allows me to page through my output. I can pipe a command to the more command, and the information is displayed one page at a time. This is useful because the pager in the Windows PowerShell ISE command output window does not work.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Hi Ed,

    Excellent article, one suggestion use:

      $hash[$a] = $a

    instead of the add and try/catch. The indexer never throws an exception but overwrites the value if the key is already present. This is shorter, clearer and if duplicates are encountered faster, since exceptions are quite costy in .net.