{"id":12401,"date":"2011-10-14T00:01:00","date_gmt":"2011-10-14T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2011\/10\/14\/create-a-powershell-quiz-by-reading-a-text-file\/"},"modified":"2011-10-14T00:01:00","modified_gmt":"2011-10-14T00:01:00","slug":"create-a-powershell-quiz-by-reading-a-text-file","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/create-a-powershell-quiz-by-reading-a-text-file\/","title":{"rendered":"Create a PowerShell Quiz by Reading a Text File"},"content":{"rendered":"<p><span style=\"font-family: Segoe\"><strong>Summary:<\/strong> Learn how to create a Windows PowerShell hash table from a text file.<\/span><\/p>\n<p><span style=\"font-family: Segoe\">&nbsp;<\/span><\/p>\n<p><span style=\"font-family: Segoe\">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).<\/span><\/p>\n<p><span style=\"font-family: Segoe\"><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8865.hsg-10-14-11-1.jpg\"><img decoding=\"async\" style=\"border: 0px\" title=\"Photo of Sean Kearney, the Scripting Wife, and...MOOSE!\" alt=\"Photo of Sean Kearney, the Scripting Wife, and...MOOSE!\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8865.hsg-10-14-11-1.jpg\" \/><\/a><\/span><\/p>\n<p><span style=\"font-family: Segoe\">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.<\/span><\/p>\n<p><span style=\"font-family: Segoe\"><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4048.hsg-10-14-11-2.jpg\"><img decoding=\"async\" style=\"border: 0px\" title=\"Photo of the Scripting Guy, the Scripting Wife, and...MOOSE!\" alt=\"Photo of the Scripting Guy, the Scripting Wife, and...MOOSE!\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4048.hsg-10-14-11-2.jpg\" \/><\/a><\/span><\/p>\n<p><span style=\"font-family: Segoe\">Following the moose incident, we drove over to Microsoft Windows Expert and IT pro MVP Mitch Garvis&rsquo;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&rsquo;s dive right in. <\/span><\/p>\n<p style=\"padding-left: 30px\"><b><span style=\"font-family: Segoe\">Note<\/span><\/b><span style=\"font-family: Segoe\">&nbsp; &nbsp;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 <\/span><a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2011\/10\/10\/cool-powershell-game-teaches-cmdlet-names.aspx\"><span style=\"color: #0000ff;font-family: segoe\">replacing random letters in a string<\/span><\/a><span style=\"font-family: Segoe\">. Next, I <\/span><a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2011\/10\/11\/use-powershell-parameter-attributes-to-avoid-errors.aspx\"><span style=\"color: #0000ff;font-family: segoe\">moved the code into a function and added parameter validation<\/span><\/a><span style=\"font-family: Segoe\"> 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&mdash;prevent the error from arising in the first place. Then, I <\/span><a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2011\/10\/12\/create-a-powershell-quiz-script.aspx\"><span style=\"color: #0000ff;font-family: segoe\">added the question and answer feature<\/span><\/a><span style=\"font-family: Segoe\"> 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 <b>New-Quiz<\/b> 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 <\/span><a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2010\/09\/11\/automatically-add-comment-based-help-to-your-powershell-scripts.aspx\"><span style=\"color: #0000ff;font-family: segoe\">Windows PowerShell ISE Add-Help function<\/span><\/a><span style=\"font-family: Segoe\">. &nbsp;<\/span><\/p>\n<p style=\"padding-left: 30px\"><b><span style=\"font-family: Segoe\">Another note&nbsp; <\/span><\/b><span style=\"font-family: Segoe\">&nbsp;This script is getting kind of long, so I uploaded the Windows PowerShell Quiz script to the <\/span><a href=\"http:\/\/gallery.technet.microsoft.com\/scriptcenter\/PowerShell-Quiz-script-e88a5766\"><span style=\"color: #0000ff;font-family: segoe\">Scripting Guys Script Repository<\/span><\/a><span style=\"font-family: Segoe\">. I also attached the questions.txt file so that you would have a good sample of what a question file might look like. <\/span><\/p>\n<p><span style=\"font-family: Segoe\">The changes to the <b>New-Question<\/b> 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 <b>@()<\/b> syntax to create my arrays. Here is the parameter section of the <b>New-Question<\/b> function:<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">Function New-Question<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">{<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">&nbsp;Param(<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">&nbsp; [hashtable]$Puzzle,<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">&nbsp; [int]$num = 10,<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">&nbsp; [string]$prompt = &#8220;What is the cmdlet name&#8221;,<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">&nbsp; [array]$rightPrompt = @(&#8220;Correct&#8221;,&#8221;equals&#8221;),<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">&nbsp; [array]$wrongPrompt = @(&#8220;Sorry.&#8221;,&#8221;is not correct&#8221;,&#8221;is&#8221;)<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">&nbsp;)<\/span><\/p>\n<p><span style=\"font-family: Segoe\">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 <b>$prompt<\/b> variable. It is the text that appears when the script runs. This prompt is shown in the following figure.<\/span><\/p>\n<p><span style=\"font-family: Segoe\"><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1362.hsg-10-14-11-3.png\"><img decoding=\"async\" style=\"border: 0px\" title=\"Image of prompt\" alt=\"Image of prompt\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1362.hsg-10-14-11-3.png\" \/><\/a><\/span><\/p>\n<p><span style=\"font-family: Segoe\">Of course, in this example, the default value does not match up. This is because the value of <b>$prompt<\/b> is overridden at the entry point to the script where I supply a new prompt value at the <i>prompt <\/i>parameter. This is shown here:<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">New-Question -puzzle $puzzle -num 5 -prompt &#8220;What is the capital of&#8221; `<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">&nbsp;&nbsp; -rightPrompt &#8220;Correct, the capital of&#8221;,&#8221;is&#8221; -wrongprompt &#8220;Sorry&#8221;,&#8221;is not correct. The capital of&#8221;,&#8221;is&#8221;<\/span><\/p>\n<p><span style=\"font-family: Segoe\">&nbsp;<\/span><\/p>\n<p><span style=\"font-family: Segoe\">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&mdash;the back tick or grave symbol&mdash;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 &ldquo;What is the capital of&rdquo; prompt and the <i>rightprompt<\/i> parameter.&nbsp; <\/span><\/p>\n<p><span style=\"font-family: Segoe\">Both the <i>rightprompt <\/i>and the <i>wrongprompt <\/i>parameters accept an array for input. When supplied from the command line, a comma separates the elements of the array. The <i>wrongprompt <\/i>uses three elements, and the <i>rightprompt <\/i>uses two elements. <\/span><\/p>\n<p><span style=\"font-family: Segoe\">If an answer is correct, the quiz element matches the value supplied from the command line. The first element of the <b>rightprompt<\/b><i> <\/i>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:<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">If($quiz.contains($rtn))<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8220;$($RightPrompt[0]) $($quiz.item($P)) $($RightPrompt[1]) $p&#8221; <\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $right ++<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;}<\/span><\/p>\n<p><span style=\"font-family: Segoe\">If the question is wrong, the <b>wrongprompt<\/b><i> <\/i>array supplies values to complete the string returned to the user. This is shown here: <\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">ELSE<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8220;$($wrongPrompt[0]) $rtn $($wrongPrompt[1]) $($quiz.item($P)) $($wrongPrompt[2]) $p&#8221; <\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $wrong ++<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">&nbsp;&nbsp;&nbsp;&nbsp; } #end foreach $P<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">&nbsp;<\/span><\/p>\n<p><span style=\"font-family: Segoe\">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. <\/span><\/p>\n<p><span style=\"font-family: Segoe\"><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1643.hsg-10-14-11-4.png\"><img decoding=\"async\" style=\"border: 0px\" title=\"Image of text file that is basis for capitals quiz\" alt=\"Image of text file that is basis for capitals quiz\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1643.hsg-10-14-11-4.png\" \/><\/a><\/span><\/p>\n<p><span style=\"font-family: Segoe\">I use the <b>ConvertFrom-StringData<\/b> cmdlet to create a hash table from a text file. There are <\/span><a href=\"http:\/\/blogs.technet.com\/search\/searchresults.aspx?q=convertfrom-stringdata&amp;sections=7618\"><span style=\"color: #0000ff;font-family: segoe\">several Hey, Scripting Guy! posts about using the ConvertFrom-StringData cmdlet<\/span><\/a><span style=\"font-family: Segoe\"> 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&rsquo;s Hey, Scripting Guy! Blog post. <\/span><\/p>\n<p><span style=\"font-family: Segoe\">Because of the way that <b>Get-Content<\/b> cmdlet works, I could not make it do what I needed for the <b>stringdata<\/b><i> <\/i>of the cmdlet. I solved the problem by using the .NET Framework <b>io.file<\/b> class and calling the static <b>ReadAllText<\/b> method. This function is shown here:<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">Function New-Puzzle<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">{<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">&nbsp;Param([string]$path)<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">&nbsp; ConvertFrom-StringData -StringData ([io.file]::ReadAllText($path))<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">} #end function new-puzzle<\/span><\/p>\n<p><span style=\"font-family: Segoe\">The entry point to the script is how you control which quiz is offered and which prompts are used. It&rsquo;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:<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">#$puzzle = New-CmdletPuzzle -level 5<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">$puzzle = New-puzzle -path C:\\fso\\Questions.txt<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">New-Question -puzzle $puzzle -num 5 -prompt &#8220;What is the capital of&#8221; `<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: Segoe\">&nbsp;&nbsp; -rightPrompt &#8220;Correct, the capital of&#8221;,&#8221;is&#8221; -wrongprompt &#8220;Sorry&#8221;,&#8221;is not correct. The capital of&#8221;,&#8221;is&#8221;<\/span><\/p>\n<p><span style=\"font-family: Segoe\">&nbsp;<\/span><\/p>\n<p><span style=\"font-family: Segoe\">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. <\/span><\/p>\n<p><span style=\"font-family: Segoe\">I invite you to follow me on <\/span><a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\"><span style=\"color: #0000ff;font-family: segoe\">Twitter<\/span><\/a><span style=\"font-family: Segoe\"> and <\/span><a href=\"http:\/\/bit.ly\/scriptingguysfacebook\"><span style=\"color: #0000ff;font-family: segoe\">Facebook<\/span><\/a><span style=\"font-family: Segoe\">. If you have any questions, send email to me at <\/span><a href=\"mailto:scripter@microsoft.com\" target=\"_blank\"><span style=\"color: #0000ff;font-family: segoe\">scripter@microsoft.com<\/span><\/a><span style=\"font-family: Segoe\">, or post your questions on the <\/span><a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\"><span style=\"color: #0000ff;font-family: segoe\">Official Scripting Guys Forum<\/span><\/a><span style=\"font-family: Segoe\">. See you tomorrow. Until then, peace.<\/span><\/p>\n<\/p>\n<p><b>Ed Wilson, Microsoft Scripting Guy<\/b><\/p>\n<p><span style=\"font-family: Segoe\"><\/span>&nbsp;<\/p>\n<p><span style=\"font-family: Segoe\">&nbsp;<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Learn how to create a Windows PowerShell hash table from a text file. &nbsp; 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 [&hellip;]<\/p>\n","protected":false},"author":596,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[292,3,4,45],"class_list":["post-12401","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-arrays-hashtables-and-dictionary-objects","tag-scripting-guy","tag-scripting-techniques","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Learn how to create a Windows PowerShell hash table from a text file. &nbsp; 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 [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/12401","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/596"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=12401"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/12401\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=12401"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=12401"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=12401"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}