Learn about Windows PowerShell
Summary: Learn how to create a Windows PowerShell hash table from a text file.
Microsoft Scripting Guy Ed Wilson here. Our Canadian trek continues. We got to meet with Microsoft MVP Sean Kearney and his wife Rose, and we spent the day roaming around Toronto. He even took me to the store where he bought his trademark BATCHman hat. Along the way, the Scripting Wife and Sean were attacked by a killer moose. As shown in the following photo, Sean struggled valiantly to protect the Scripting Wife (no moose were actually used for this photo, and no one was harmed in the taking of this picture).
After Sean explained his intentions to the moose, we were all on the best of terms. Sean even captured the moment as the Scripting Wife and I discussed plans with the moose for use in an upcoming series of Hey Scripting Guy articles. The negotiations were intense, but in the end, we were all friends.
Following the moose incident, we drove over to Microsoft Windows Expert and IT pro MVP Mitch Garvis’s house where his wife Theresa fixed a sumptuous banquet. Microsoft Windows Azure MVP Cory Fowler stopped by, and soon the conversation turned to travel and Windows PowerShell. Stay tuned because Cory has agreed to write a guest blog article. In between hobnobbing with moose and hanging out with MVPs, I did get some time to work on my Windows PowerShell quiz script. I am anxious to share it with you, so let’s dive right in.
Note This is the fifth part of a multipart series of articles about writing a Windows PowerShell quiz script. On the first day, I looked at replacing random letters in a string. Next, I moved the code into a function and added parameter validation to limit the values that can be supplied to the function. In this way I was able to prevent a divide by zero error that could arise depending on what someone supplied from the command line. This is actually a great way to do error handling—prevent the error from arising in the first place. Then, I added the question and answer feature for the Windows PowerShell cmdlet name game. And then I added the ability to choose a specific number of questions, as well as a grade feature. Today I am going to add a New-Quiz function, and clean up the code to permit different prompt strings. The one thing I am not going to have time to do is to add comment-based help, but that is really easy to do given my Windows PowerShell ISE Add-Help function.
Another note This script is getting kind of long, so I uploaded the Windows PowerShell Quiz script to the Scripting Guys Script Repository. I also attached the questions.txt file so that you would have a good sample of what a question file might look like.
The changes to the New-Question function are extensive, but not particularly complex. I abstracted the prompts from hard-coded text to variables. This allows for a great deal of flexibility. Not only can I use the quiz script to offer different quizzes, but I can even localize the language easily when calling the script. In addition, I wanted to keep the simplicity of the earlier scripts, so I set default values that are equivalent to the earlier scripts. One thing that was a bit tricky was supplying an array to the prompts. I used the @() syntax to create my arrays. Here is the parameter section of the New-Question function:
[int]$num = 10,
[string]$prompt = "What is the cmdlet name",
[array]$rightPrompt = @("Correct","equals"),
[array]$wrongPrompt = @("Sorry.","is not correct","is")
One reason I like to explicitly cast the parameters is that when using comment-based help, the help subsystem is smart enough to pick up the type constraints and use that information in the help output. It also helps to prevent errors. The first prompt is stored in the $prompt variable. It is the text that appears when the script runs. This prompt is shown in the following figure.
Of course, in this example, the default value does not match up. This is because the value of $prompt is overridden at the entry point to the script where I supply a new prompt value at the prompt parameter. This is shown here:
New-Question -puzzle $puzzle -num 5 -prompt "What is the capital of" `
-rightPrompt "Correct, the capital of","is" -wrongprompt "Sorry","is not correct. The capital of","is"
One thing to keep in mind is the use of line continuation. I hate using line continuation because it is always an extra level of complexity. In this case, the command line would be too long to be easily read on the blog, so I use the line continuation character—the back tick or grave symbol—after the close of the prompt string. If you have a wide screen and a small enough font, this line of code will fit on a single line. Remove the back tick character and remove the spaces between the end of the “What is the capital of” prompt and the rightprompt parameter.
Both the rightprompt and the wrongprompt parameters accept an array for input. When supplied from the command line, a comma separates the elements of the array. The wrongprompt uses three elements, and the rightprompt uses two elements.
If an answer is correct, the quiz element matches the value supplied from the command line. The first element of the rightprompt array states the value is correct, and the value that is matched is displayed from the quiz hash table. The second element of the prompt is the verb that is appropriate to the question. This portion of the function is shown here:
"$($RightPrompt) $($quiz.item($P)) $($RightPrompt) $p"
If the question is wrong, the wrongprompt array supplies values to complete the string returned to the user. This is shown here:
"$($wrongPrompt) $rtn $($wrongPrompt) $($quiz.item($P)) $($wrongPrompt) $p"
} #end foreach $P
In the script I uploaded to the Scripting Guys Script Repository, I included a text file (shown in the following figure) that provides the basis of a quiz on capitals. The capitals are not listed in any particular order. They are simply the ones that came up as I was playing around with Bing looking for capitals.
I use the ConvertFrom-StringData cmdlet to create a hash table from a text file. There are several Hey, Scripting Guy! posts about using the ConvertFrom-StringData cmdlet to create a hash table, and I recommend them for background information about using this technique. In fact, I like this topic so much, I am also talking about it in Sunday’s Hey, Scripting Guy! Blog post.
Because of the way that Get-Content cmdlet works, I could not make it do what I needed for the stringdata of the cmdlet. I solved the problem by using the .NET Framework io.file class and calling the static ReadAllText method. This function is shown here:
ConvertFrom-StringData -StringData ([io.file]::ReadAllText($path))
} #end function new-puzzle
The entry point to the script is how you control which quiz is offered and which prompts are used. It’s up to you whether you hard-code them as I did here, or you simply type them from the command line when calling the script, but I thought adding the actual command lines here was useful because the syntax of the prompts can take a bit of experimentation until it makes sense. This is something I would not expect people to do on the fly. One way to solve the problem is to comment out a line when it is not used; this is what I did here:
#$puzzle = New-CmdletPuzzle -level 5
$puzzle = New-puzzle -path C:\fso\Questions.txt
That is all there is for today. Join me tomorrow when I will go over more detail about hash tables (our informal topic for the week). Hope to see you then.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at firstname.lastname@example.org, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy
And believe me, that moose had one nasty "Byte", I think he took a good "Nybble" out of my hat. There wasn't one "Bit" left. ;)