{"id":72172,"date":"2015-07-14T00:01:00","date_gmt":"2015-07-14T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2015\/07\/14\/more-powershell-script-tracing-and-strict-mode\/"},"modified":"2019-02-18T09:47:00","modified_gmt":"2019-02-18T16:47:00","slug":"more-powershell-script-tracing-and-strict-mode","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/more-powershell-script-tracing-and-strict-mode\/","title":{"rendered":"More PowerShell Script Tracing and Strict Mode"},"content":{"rendered":"<p><b style=\"font-size:12px\">Summary<\/b><span style=\"font-size:12px\">: Ed Wilson, Microsoft Scripting Guy, talks more about Windows PowerShell script tracing and enabling strict mode.<\/span><\/p>\n<p>Microsoft Scripting Guy, Ed Wilson, is here. It can be somewhat painful to troubleshoot intermittent errors with Windows PowerShell. Two things that can lead to this are variables that do not get initialized properly (and therefore their values change over subsequent running of the script) and functions that do not get accessed properly. To solve both of these problems, we can use Windows PowerShell cmdlets.<\/p>\n<p>Yesterday in <a href=\"https:\/\/devblogs.microsoft.com\/scripting\/tracing-the-execution-of-a-powershell-script\/\" target=\"_blank\">Tracing the Execution of a PowerShell Script<\/a>, I introduced the tracing levels and discussed working with trace level 1. Today, I&#039;ll investigate working with trace level 2.<\/p>\n<p class=\"Normalunindented\">When the trace level is set to 2, each line in the script that executes is displayed in the Windows PowerShell console. In addition, each variable assignment, function call, and outside script call is displayed. These additional tracing details are prefixed with an exclamation mark to make them easier to see. When the <b>-trace<\/b> parameter from <b>Set-PSDebug <\/b>is set to 2, an extra line is displayed, indicating a variable assignment.<\/p>\n<p>When I run the CreateRegistryKey.ps1 script (which I published yesterday), the function trace points first to the script, stating that it is calling a function called CreateRegistryKey.ps1. Calls to functions are prefixed with <b>! CALL<\/b>, which makes them easy to see.<\/p>\n<p>Windows PowerShell treats scripts as functions. The next function that is called is the <b>Add-RegistryValue<\/b> function. The trace also states where the function is defined by indicating the path to the file. This is shown here:<\/p>\n<p style=\"margin-left:30px\">PS C:\\&gt; Set-PSDebug -Trace 2<\/p>\n<p style=\"margin-left:30px\">PS C:\\&gt; C:\\fso\\CreateRegistryKey.ps1<\/p>\n<p style=\"margin-left:30px\">DEBUG:&nbsp;&nbsp;&nbsp; 1+&nbsp; &gt;&gt;&gt;&gt; C:\\fso\\CreateRegistryKey.ps1<\/p>\n<p style=\"margin-left:30px\">DEBUG:&nbsp;&nbsp;&nbsp;&nbsp; ! CALL function &#039;&lt;ScriptBlock&gt;&#039;<\/p>\n<p style=\"margin-left:30px\">DEBUG:&nbsp;&nbsp; 30+&nbsp; &gt;&gt;&gt;&gt; Add-RegistryValue -key forscripting -value test<\/p>\n<p style=\"margin-left:30px\">DEBUG:&nbsp;&nbsp;&nbsp;&nbsp; ! CALL function &#039;&lt;ScriptBlock&gt;&#039;&nbsp; (defined in file<\/p>\n<p style=\"margin-left:30px\">&#039;C:\\fso\\CreateRegistryKey.ps1&#039;)<\/p>\n<p style=\"margin-left:30px\">DEBUG:&nbsp;&nbsp; 12+&nbsp; &gt;&gt;&gt;&gt; {<\/p>\n<p style=\"margin-left:30px\">DEBUG:&nbsp;&nbsp;&nbsp;&nbsp; ! CALL function &#039;Add-RegistryValue&#039;&nbsp; (defined in file<\/p>\n<p style=\"margin-left:30px\">&#039;C:\\fso\\CreateRegistryKey.ps1&#039;)<\/p>\n<p>The <b>! SET<\/b> keyword is used to preface variable assignments. The first variable that is assigned is the <b>$scriptRoot<\/b> variable:<\/p>\n<p class=\"CodeBlock\" style=\"margin-left:30px\">DEBUG:&nbsp;&nbsp;&nbsp;&nbsp; ! SET $scriptRoot = &#039;HKCU:\\software\\ForScripting&#039;.<br \/> DEBUG:&nbsp;&nbsp; 15+&nbsp; if( &gt;&gt;&gt;&gt; -not (Test-Path -path $scriptRoot))<br \/> DEBUG:&nbsp;&nbsp; 23+&nbsp;&nbsp;&nbsp;&nbsp; &gt;&gt;&gt;&gt; Set-ItemProperty -Path $scriptRoot -Name $key -Value<br \/> $value | `<br \/> DEBUG:&nbsp;&nbsp; 27+&nbsp; &gt;&gt;&gt;&gt; } #end function Add-RegistryValue<br \/> PS C:\\&gt;<\/p>\n<p>When the CreateRegistryKey.ps1 script is run with trace level 2, the detailed tracing shown here is displayed:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/40182.1.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/40182.1.png\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<h2>Enabling strict mode<\/h2>\n<p class=\"Normalunindented\">One easily correctable problem that can cause debugging nightmares in a script involves variables. Variables are often used incorrectly, are nonexistent, or are initialized improperly. An easy mistake to make when using variables is a simple typing error. Simple typing errors can also cause problems when they are contained in a large complex script.<\/p>\n<p class=\"Normalunindented\">Enabling strict mode causes Windows PowerShell to display an error if a variable is not declared. This helps you avoid the problem of nonexistent or improperly initialized variables.<\/p>\n<h3>Using Set-PSDebug -Strict<\/h3>\n<p class=\"Normalunindented\">An example of a simple typing error in a script is shown in the SimpleTypingError.ps1 script:<\/p>\n<p class=\"CodeBlockHead\" style=\"margin-left:30px\">SimpleTypingError.ps1<\/p>\n<p class=\"CodeBlock\" style=\"margin-left:30px\">$a = 2<br \/> $b = 5<br \/> $d = $a + $b<br \/> &#039;The value of $c is: &#039; + $c<\/p>\n<p>When the SimpleTypingError.ps1 script is run, the following output is shown:<\/p>\n<p class=\"CodeBlock\" style=\"margin-left:30px\">PS C:\\&gt; C:\\fso\\SimpleTypingError.ps1<br \/> The value of $c is:<br \/> PS C:\\&gt;<\/p>\n<p>As you can see, the value of the <b>$c<\/b> variable is not displayed. If you use the <b>-Strict<\/b> parameter from the <b>Set-PSDebug<\/b> cmdlet, an error is generated. The error tells you that the value of <b>$c<\/b> has not been set:<\/p>\n<p style=\"margin-left:30px\">PS C:\\&gt; Set-PSDebug -Strict<\/p>\n<p style=\"margin-left:30px\">PS C:\\&gt; C:\\fso\\SimpleTypingError.ps1<\/p>\n<p style=\"margin-left:30px\">The variable &#039;$c&#039; cannot be retrieved because it has not been set.<\/p>\n<p style=\"margin-left:30px\">At C:\\fso\\SimpleTypingError.ps1:13 char:26<\/p>\n<p style=\"margin-left:30px\">+ &#039;The value of $c is: &#039; + $c<\/p>\n<p style=\"margin-left:30px\">+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ~~<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; + CategoryInfo&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : InvalidOperation: (c:String) [], RuntimeExceptio<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp; n<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; + FullyQualifiedErrorId : VariableIsUndefined<\/p>\n<p>When you go back to the SimpleTypingError.ps1 script and examine it, you can see that the sum of <b>$a<\/b> and <b>$b<\/b> was assigned to <b>$d<\/b>, not <b>$c<\/b>. The way to correct the problem is to assign the sum of <b>$a<\/b> and <b>$b<\/b> to <b>$c<\/b> instead of <b>$d<\/b> (which was probably the original intention).<\/p>\n<p>It is possible to include the <b>Set-PSDebug -Strict<\/b> command in your scripts to provide a quick check for uninitialized variables while you are actually writing the script. You can therefore avoid the error completely.<\/p>\n<p>If you routinely use an expanding string to display the value of your variables, you need to be aware that an uninitialized variable is not reported as an error. The SimpleTypingErrorNotReported.ps1 script uses an expanding string to display the value of the <b>$c<\/b> variable.<\/p>\n<p>The first instance of the <b>$c<\/b> variable is escaped by the use of the backtick character ( ` ). This causes the variable name to be displayed, and does not expand its value. The second occurrence of the <b>$c<\/b> variable is expanded. Here is the line of code that does this:<\/p>\n<p class=\"CodeBlock\" style=\"margin-left:30px\">&quot;The value of `$c is: $c&quot;<\/p>\n<p>When the SimpleTypingErrorNotReported.ps1 script is run, the following is displayed:<\/p>\n<p style=\"margin-left:30px\">PS C:\\&gt; Set-PSDebug -Strict<\/p>\n<p style=\"margin-left:30px\">PS C:\\&gt; C:\\fso\\SimpleTypingErrorNotReported.ps1<\/p>\n<p>The value of $c is:<\/p>\n<p style=\"margin-left:30px\">PS C:\\&gt;<\/p>\n<p>The complete SimpleTypingErrorNotReported.ps1 script is shown here:<\/p>\n<p class=\"CodeBlockHead\">SimpleTypingErrorNotReported.ps1<\/p>\n<p class=\"CodeBlock\" style=\"margin-left:30px\">$a = 2<br \/> $b = 5<br \/> $d = $a + $b<br \/> &quot;The value of `$c is: $c&quot;<\/p>\n<p>To disable strict mode, you use the <b>Set-PSDebug -off<\/b> command.<\/p>\n<p>Now you know how to use trace level 2 and strict mode in a Windows PowerShell script. Debugging Week will continue tomorrow when I will talk about more cool debugging stuff.<\/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\" target=\"_blank\">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><b>Ed Wilson, Microsoft Scripting Guy<\/b><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Ed Wilson, Microsoft Scripting Guy, talks more about Windows PowerShell script tracing and enabling strict mode. Microsoft Scripting Guy, Ed Wilson, is here. It can be somewhat painful to troubleshoot intermittent errors with Windows PowerShell. Two things that can lead to this are variables that do not get initialized properly (and therefore their values [&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":[294,3,4,45],"class_list":["post-72172","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-debugging","tag-scripting-guy","tag-scripting-techniques","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Ed Wilson, Microsoft Scripting Guy, talks more about Windows PowerShell script tracing and enabling strict mode. Microsoft Scripting Guy, Ed Wilson, is here. It can be somewhat painful to troubleshoot intermittent errors with Windows PowerShell. Two things that can lead to this are variables that do not get initialized properly (and therefore their values [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/72172","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=72172"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/72172\/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=72172"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=72172"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=72172"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}