{"id":12421,"date":"2011-10-12T00:01:00","date_gmt":"2011-10-12T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2011\/10\/12\/create-a-powershell-quiz-script\/"},"modified":"2011-10-12T00:01:00","modified_gmt":"2011-10-12T00:01:00","slug":"create-a-powershell-quiz-script","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/create-a-powershell-quiz-script\/","title":{"rendered":"Create a PowerShell Quiz Script"},"content":{"rendered":"<p><strong>Summary:<\/strong> Learn how to use hash tables and create a Windows PowerShell quiz script.<\/p>\n<p>&nbsp;<\/p>\n<p>Microsoft Scripting Guy Ed Wilson here. For the past several days, I have been building a Windows PowerShell cmdlet name quiz. On the first day, I looked at <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2011\/10\/10\/cool-powershell-game-teaches-cmdlet-names.aspx\">replacing random letters in a string<\/a>. Next I <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2011\/10\/11\/use-powershell-parameter-attributes-to-avoid-errors.aspx\">moved the code into a function and&nbsp; dded parameter validation<\/a> 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.&nbsp; Today, I am going to add the question and answer feature for the Windows PowerShell cmdlet name game.<\/p>\n<p>The complete New-CmdletPuzzleQuiz.ps1 script is shown here.<\/p>\n<p style=\"padding-left: 30px\">function New-CmdletPuzzle<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Param(<\/p>\n<p style=\"padding-left: 30px\">&nbsp; [Parameter(Position=0,<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HelpMessage=&#8221;A number between 2 and 7&#8243;)]<\/p>\n<p style=\"padding-left: 30px\">&nbsp; [alias(&#8220;Level&#8221;)]<\/p>\n<p style=\"padding-left: 30px\">&nbsp; [ValidateRange(2,7)]<\/p>\n<p style=\"padding-left: 30px\">&nbsp; [int]$difficulty = 4<\/p>\n<p style=\"padding-left: 30px\">&nbsp;) #end param<\/p>\n<p style=\"padding-left: 30px\">&nbsp;$array = @()<\/p>\n<p style=\"padding-left: 30px\">&nbsp;$hash = New-Object hashtable<\/p>\n<p style=\"padding-left: 30px\">&nbsp;$array = Get-Command -CommandType cmdlet |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;ForEach-Object { $_.name.tostring() }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Foreach($cmdlet in $array)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;{<\/p>\n<p style=\"padding-left: 30px\">&nbsp; $rndChar = get-random -InputObject ($cmdlet.tochararray()) `<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -count ($cmdlet.length\/$difficulty)<\/p>\n<p style=\"padding-left: 30px\">&nbsp; $cmdletP = $cmdlet #moved from inside foreach loop<\/p>\n<p style=\"padding-left: 30px\">&nbsp; foreach($l in $rndchar)<\/p>\n<p style=\"padding-left: 30px\">&nbsp; {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; #moved code to outside Foreach loop<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; $cmdletP = $cmdletP.Replace($l,&#8221;_&#8221;)<\/p>\n<p style=\"padding-left: 30px\">&nbsp; }# end foreach l<\/p>\n<p style=\"padding-left: 30px\">&nbsp; $hash.add($cmdlet,$cmdletP)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;} #end foreach cmdlet<\/p>\n<p style=\"padding-left: 30px\">&nbsp;$hash<\/p>\n<p style=\"padding-left: 30px\">}<br \/>#end function New-CmdletPuzzle<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">Function New-Question<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Param(<\/p>\n<p style=\"padding-left: 30px\">&nbsp; [hashtable]$Puzzle<\/p>\n<p style=\"padding-left: 30px\">&nbsp;)<\/p>\n<p style=\"padding-left: 30px\">&nbsp; Foreach ($p in $puzzle.KEYS)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; $rtn = Read-host &#8220;What is the cmdlet name $($puzzle.item($P))&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; If($puzzle.contains($rtn))<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp; { &#8220;Correct $($puzzle.item($P)) equals $p&#8221; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; ELSE<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp; {&#8220;Sorry. $rtn is not right. $($puzzle.item($P)) is $p&#8221; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; } #end foreach $P<\/p>\n<p style=\"padding-left: 30px\">}<br \/>#end function New-Question<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">#<br \/>*** Entry point to script ***<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">$puzzle = New-CmdletPuzzle<\/p>\n<p style=\"padding-left: 30px\">New-Question -puzzle $puzzle<\/p>\n<p>&nbsp;<\/p>\n<p>The first thing I do in the New-CmdletPuzzleQuiz.ps1 script is use the <b>New-CmdletPuzzle<\/b> function from <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2011\/10\/11\/use-powershell-parameter-attributes-to-avoid-errors.aspx\">yesterday&rsquo;s script<\/a>. That function creates a hash table with cmdlet names as the key value and cmdlet names with missing letters as the value. It then returns the hash table to&nbsp; he calling code.<\/p>\n<p>The new function I wrote for today is the New-Question function. It appears here.<\/p>\n<p style=\"padding-left: 30px\">Function New-Question<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Param(<\/p>\n<p style=\"padding-left: 30px\">&nbsp; [hashtable]$Puzzle<\/p>\n<p style=\"padding-left: 30px\">&nbsp;)<\/p>\n<p style=\"padding-left: 30px\">&nbsp; Foreach ($p in $puzzle.KEYS)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; $rtn = Read-host &#8220;What is the cmdlet name $($puzzle.item($P))&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; If($puzzle.contains($rtn))<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp; { &#8220;Correct $($puzzle.item($P)) equals $p&#8221; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; ELSE<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp; {&#8220;Sorry. $rtn is not right. $($puzzle.item($P)) is $p&#8221; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; } #end foreach $P<\/p>\n<p style=\"padding-left: 30px\">}<br \/>#end function New-Question<\/p>\n<p>&nbsp;<\/p>\n<p>The input to the <b>New-Question<\/b> function is the hash table returned by the <b>New-CmdletPuzzle <\/b>function, but any question\/answer type of hash table would actually work. For example, in the following script, QuestionsAndAnswere.ps1, I add a function that creates a hash table of&nbsp; questions about capitals and their associated countries. The only change I needed to make to the <b>New-Question<\/b> function was to remove the phrase, &ldquo;What is the cmdlet name&rdquo; from the <b>Read-Host<\/b> command<\/p>\n<p>This actually points to a major design issue&mdash;hard coded literals often cause code reuse issues. In this example, if I had a variable to hold the prompt string, and I passed the string when calling the function, it would be easier to reuse the function. The two evaluation strings (&ldquo;Correct&hellip;equals&#8230;&rdquo; and &ldquo;Sorry&hellip;Is not right&hellip;is&hellip;&rdquo;) are pretty generic and make sense (albeit a bit stilted) in most cases. A better approach there would be to use custom correct and incorrect strings, and pass them when calling the function. The more abstract a function becomes, the greater the reuse capabilities. The complete QuestionsAndAnswers.ps1 script is shown here.<\/p>\n<p style=\"padding-left: 30px\"><b>QuestionsAndAnswers.ps1<\/b><\/p>\n<p style=\"padding-left: 30px\">Function New-Puzzle<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;@{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; &#8220;What is the capital of Australia&#8221; = &#8220;Canberra&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; &#8220;What is the capital of Canada&#8221; = &#8220;Ottawa&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; &#8220;What is the capital of Germany&#8221; = &#8220;Berlin&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p style=\"padding-left: 30px\">Function New-Question<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Param(<\/p>\n<p style=\"padding-left: 30px\">&nbsp; [hashtable]$Puzzle<\/p>\n<p style=\"padding-left: 30px\">&nbsp;)<\/p>\n<p style=\"padding-left: 30px\">&nbsp; Foreach ($p in $puzzle.KEYS)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; $rtn = Read-host &#8220;$($puzzle.item($P))&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; If($puzzle.contains($rtn))<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp; { &#8220;Correct $($puzzle.item($P)) equals $p&#8221; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; ELSE<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp; {&#8220;Sorry. $rtn is not right. $($puzzle.item($P)) is $p&#8221; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; } #end foreach $P<\/p>\n<p style=\"padding-left: 30px\">}<br \/>#end function New-Question<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">#<br \/>*** Entry point to script ***<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">$puzzle = New-Puzzle<\/p>\n<p style=\"padding-left: 30px\">New-Question -puzzle $puzzle<\/p>\n<p>&nbsp;<\/p>\n<p>In the <b>New-Question<\/b> function, I use a <b>[hashtable]<\/b> type constraint to ensure the input parameter is a hash table. This code is shown here:<\/p>\n<p style=\"padding-left: 30px\">Function New-Question<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Param(<\/p>\n<p style=\"padding-left: 30px\">&nbsp; [hashtable]$Puzzle<\/p>\n<p style=\"padding-left: 30px\">&nbsp;)<\/p>\n<p>When I have the input hash table, I use the <b>foreach <\/b>language statement to walk through the collection of hash table keys. I obtain the hash table keys by using the <b>keys<\/b><i> <\/i>property from the <b>hashtable<\/b> object. I use the variable <b>$p<\/b> to represent a single key (the enumerator) in the collection as I work my way through the collection. This portion of the <b>foreach<\/b> loop is shown here:<\/p>\n<p style=\"padding-left: 30px\">Foreach ($p in $puzzle.KEYS)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; {<\/p>\n<p>I needed a way to receive input from the user of the script, and I decided that using the <b>Read-Host<\/b> cmdlet was the easiest for this application. The user types the answer to the question, and I store the answer in the <b>$rtn <\/b>variable. The <b>Read-Host<\/b> cmdlet creates an input box when run from the Windows PowerShell ISE. This box is shown in the following figure.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/3157.hsg-10-12-11-1.png\"><img decoding=\"async\" style=\"border: 0px\" title=\"Image of created input box\" alt=\"Image of created input box\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/3157.hsg-10-12-11-1.png\" \/><\/a><\/p>\n<p>When the script runs from the Windows PowerShell console, the <b>Read-Host<\/b> cmdlet generates a command-line prompt. This prompt is shown in the following figure.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/6237.hsg-10-12-11-2.png\"><img decoding=\"async\" style=\"border: 0px\" title=\"Image of created command-line prompt\" alt=\"Image of created command-line prompt\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/6237.hsg-10-12-11-2.png\" \/><\/a><\/p>\n<p>To display the cmdlet name with the random letters removed, I use the actual cmdlet name I received from the collection of keys. The variable <b>$p<\/b> contains the actual cmdlet name. When working with a hash table, the <b>item <\/b>method uses a key to retrieve the data stored in the value that is associated with the key. This concept is illustrated in the following figure.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8105.hsg-10-12-11-3.png\"><img decoding=\"async\" style=\"border: 0px\" title=\"Image of illustration of concept\" alt=\"Image of illustration of concept\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8105.hsg-10-12-11-3.png\" \/><\/a><\/p>\n<p>This line of code is shown here:<\/p>\n<p style=\"padding-left: 30px\">$rtn = Read-host &#8220;What is the cmdlet name $($puzzle.item($P))&#8221;<\/p>\n<p>&nbsp;<\/p>\n<p>When I have the user input, it is time to see if the input matches the actual cmdlet name. I do this by using the <b>contains<\/b><i> <\/i>method from the <b>hashtable<\/b> object. If the input matches, I display a line that states the user is correct; if it does not match, the <b>else<\/b><i> <\/i>condition matches. The <b>contains<\/b> operator here is case sensitive. Therefore, <b>New-Object <\/b>does not match <b>New-Object<\/b>. The <b>if<\/b><i> <\/i>portion of the script is shown here:<\/p>\n<p style=\"padding-left: 30px\">&nbsp; If($puzzle.contains($rtn))<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp; { &#8220;Correct $($puzzle.item($P)) equals $p&#8221; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; ELSE<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp; {&#8220;Sorry. $rtn is not right. $($puzzle.item($P)) is $p&#8221; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; } #end foreach $P<\/p>\n<p style=\"padding-left: 30px\">}<br \/>#end function New-Question<\/p>\n<p>&nbsp;<\/p>\n<p>The entry point to the script is pretty simple. I call the <b>New-CmdletPuzzle<\/b> function and store the returned hash table in the <b>$puzzle<\/b> variable. I then pass the <b>$puzzle<\/b> variable containing the hash table to the <b>New-Question<\/b> function.<\/p>\n<p>&nbsp;<\/p>\n<p>That&rsquo;s it for today. Join me tomorrow when I will add the capability to determine the number of questions that make up a quiz, and return a score for the quiz.<\/p>\n<p>I invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"mailto:scripter@microsoft.com\" target=\"_blank\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.<\/p>\n<p>&nbsp;<\/p>\n<p><b>Ed Wilson, Microsoft Scripting Guy<\/b><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Learn how to use hash tables and create a Windows PowerShell quiz script. &nbsp; Microsoft Scripting Guy Ed Wilson here. For the past several days, I have been building a Windows PowerShell cmdlet name quiz. On the first day, I looked at replacing random letters in a string. Next I moved the code into [&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-12421","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 use hash tables and create a Windows PowerShell quiz script. &nbsp; Microsoft Scripting Guy Ed Wilson here. For the past several days, I have been building a Windows PowerShell cmdlet name quiz. On the first day, I looked at replacing random letters in a string. Next I moved the code into [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/12421","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=12421"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/12421\/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=12421"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=12421"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=12421"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}