{"id":8531,"date":"2007-02-23T00:47:42","date_gmt":"2007-02-23T00:47:42","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/powershell\/2007\/02\/23\/leveraging-powershell-semantics-to-do-more-with-less\/"},"modified":"2019-02-18T13:20:44","modified_gmt":"2019-02-18T20:20:44","slug":"leveraging-powershell-semantics-to-do-more-with-less","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/powershell\/leveraging-powershell-semantics-to-do-more-with-less\/","title":{"rendered":"Leveraging PowerShell Semantics to do More with Less"},"content":{"rendered":"<p><a href=\"http:\/\/www.agileprogrammer.com\/dotnetguy\/\">Brad Wilson<\/a> \u2013 the .NET Guy <a href=\"http:\/\/www.agileprogrammer.com\/dotnetguy\/archive\/2007\/02\/22\/22240.aspx\">recently posted a set of scripts<\/a> showing how to drive TortoiseSVN from PowerShell.  I thought I would use one of these to highlight how you can leverage the PowerShell semantics to do more with less code.\n<\/p>\n<p>PowerShell is provides <strong>Shell<\/strong> semantics and sometimes allows you to ask for more formal, <strong>programming<\/strong>, semantics.  Specifically, if you ask for a variable that does not exist \u2013 PowerShell does not consider that an error.  In the same token, if you ask for a PROPERTY of a variable and that property does not exist, that is not an error either.  PowerShell doesn&#8217;t complain \u2013 we give you a null and keep on going.\n<\/p>\n<p>There are times that this is a pain (e.g. if you just mistype a variable name, that can be hard to track down) so you can turn on STRICT mode by typing:\n<\/p>\n<p>  PS&gt;  SET-PSDEBUG \u2013STRICT\n<\/p>\n<p>This tells PowerShell to throw an exception if you access a variable that does not exist.  Over time, we&#8217;ll offer you more and more options to be more formal in your programming style.  That said, there are some huge advantages to the shell semantics so let&#8217;s explore them for a second.  Consider the following script:\n<\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"font-family:Verdana\"><span style=\"text-decoration:underline\"><strong>tdiff.ps1<\/strong><\/span> (used to diff arbitrary files with TortoiseMerge):\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"font-family:Courier New\">if ($args.Length -lt 2) {\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"font-family:Courier New\">    write-host &#8220;usage: tdiff &lt;file1&gt; &lt;file2&gt;&#8221;\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"font-family:Courier New\">    return\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"font-family:Courier New\">}\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\">\n\u00a0<\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"color:red;font-family:Courier New\">if ((test-path &#8220;HKLM:\\Software\\TortoiseSVN&#8221;) -eq $false) {\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"color:red;font-family:Courier New\">    write-host -foregroundColor Red &#8220;Error: Could not find TortoiseMerge.exe&#8221;\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"color:red;font-family:Courier New\">    return\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"color:red;font-family:Courier New\">}\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\">\n\u00a0<\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"color:red;font-family:Courier New\">$tortoiseKey = get-itemproperty &#8220;HKLM:\\Software\\TortoiseSVN&#8221;\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\">\n\u00a0<\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"color:red;font-family:Courier New\">if ($tortoiseKey -eq $null) {\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"color:red;font-family:Courier New\">    write-host -foregroundColor Red &#8220;Error: Could not find TortoiseMerge.exe&#8221;\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"color:red;font-family:Courier New\">    return\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"color:red;font-family:Courier New\">}\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\">\n\u00a0<\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"color:red;font-family:Courier New\">$tortoise = $tortoiseKey.TMergePath\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\">\n\u00a0<\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"color:red;font-family:Courier New\">if ($tortoise -eq $null) {\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"color:red;font-family:Courier New\">    write-host -foregroundColor Red &#8220;Error: Could not find TortoiseMerge.exe&#8221;\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"color:red;font-family:Courier New\">    return\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"color:red;font-family:Courier New\">}\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\">\n\u00a0<\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"font-family:Courier New\">$commandLine = &#8216;\/base:&#8221;&#8216; + $args[0] + &#8216;&#8221; \/mine:&#8221;&#8216; + $args[1] + &#8216;&#8221;&#8216;\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"font-family:Courier New\">&amp; $tortoise $commandLine\n<\/span><\/p>\n<p>\n\u00a0<\/p>\n<p>Notice this script has 3 validity checks (hightlighted in red) and it generates the same error message for all those checks.  It looks for the existence of a key in the registry, it ensures that that key has properties, and then it looks for a specific property.  If any of these don&#8217;t work, it generates an error message and returns.  We can do all three checks in a single step below:\n<\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"font-family:Verdana\"><span style=\"text-decoration:underline\"><strong>tdiff.ps1<\/strong><\/span> (used to diff arbitrary files with TortoiseMerge):\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"font-family:Courier New\">if ($args.Length -lt 2) {\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"font-family:Courier New\">    write-host &#8220;usage: tdiff &lt;file1&gt; &lt;file2&gt;&#8221;\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"font-family:Courier New\">    return\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"font-family:Courier New\">}\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\">\n\u00a0<\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"color:red;font-family:Courier New\">$tortoise = (Get-ItemProperty &#8220;HKLM:\\Software\\TortoiseSVN&#8221; \u2013ea SilentlyContinue).TMergePath\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\">\n\u00a0<\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"font-family:Courier New\">if ($tortoise -eq $null) {\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"font-family:Courier New\">    write-host -foregroundColor Red &#8220;Error: Could not find TortoiseMerge.exe&#8221;\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"font-family:Courier New\">    return\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"font-family:Courier New\">}\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\">\n\u00a0<\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"font-family:Courier New\">$commandLine = &#8216;\/base:&#8221;&#8216; + $args[0] + &#8216;&#8221; \/mine:&#8221;&#8216; + $args[1] + &#8216;&#8221;&#8216;\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"font-family:Courier New\">&amp; $tortoise $commandLine\n<\/span><\/p>\n<p>\n\u00a0<\/p>\n<p>The Cmdlet Get-ItemProperty generates an error if you try to access a property that does not exist.  We override that behavior with \u2013EA (this is an alias for \u2013ErrorAction).  When we specify &#8220;\u2013EA SilentlyContinue&#8221;, the cmdlet does not generate an error and returns a NULL.  We put this command in parenthesis which says, &#8220;run this command and use its results here&#8221;.  This returns a NULL and then we ask for the &#8220;TMergePath&#8221; property of a null object \u2013 which is NULL of course.\n<\/p>\n<p>These semantics allow us to significantly reduce the code required to implement this function.  It is now easier to read, understand and maintain.\n<\/p>\n<p>BTW \u2013 one last thing.  Instead of doing a WRITE-HOST \u2013FOREGROUNDCOLOR RED followed by a RETURN, you can just use a THROW\n<\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"font-family:Courier New\">$tortoise = (Get-ItemProperty &#8220;HKLM:\\Software\\TortoiseSVN&#8221; \u2013ea SilentlyContinue).TMergePath\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\">\n\u00a0<\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"font-family:Courier New\">if ($tortoise -eq $null) {\n<\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"font-family:Courier New\">\n\t\t\t<span style=\"color:red\">Throw &#8220;Error: Could not find TortoiseMerge.exe&#8221;\n<\/span><\/span><\/p>\n<p style=\"background: #eeeeee;margin-left: 36pt\"><span style=\"color:red;font-family:Courier New\">}\n<\/span><\/p>\n<p>\n\u00a0<\/p>\n<p>This is simpler, less code, it records the error in $ERROR and it sets the $? Variable so a calling program can switch based upon the success or failure of the operation.  Also, recording errors in $ERROR also provides invocation information so it makes it easier to debug where things went wrong.  E.g.\n<\/p>\n<p style=\"margin-left: 36pt\"><span style=\"font-family:Courier New\"><br \/>PS&gt; cat function:t<br \/>$error = &#8220;Error: Something went wrong&#8221;<br \/>if ($args[0] %2)<br \/>{   throw $error<br \/>}else<br \/>{   throw $error<br \/>}<\/p>\n<p>PS&gt; t 1<br \/><span style=\"color:red\">Error: Something went wrong<br \/>At C:\\ps\\t1.ps1:5 char:10<br \/>+ {   throw &lt;&lt;&lt;&lt;  $error<br \/><\/span>PS&gt; t 2<br \/><span style=\"color:red\">Error: Something went wrong<br \/>At C:\\ps\\t1.ps1:7 char:10<br \/>+ {   throw &lt;&lt;&lt;&lt;  $error<br \/><\/span>PS&gt; $error[0].errorrecord.InvocationInfo<\/p>\n<p>MyCommand        :<br \/>ScriptLineNumber : 7<br \/>OffsetInLine     : 10<br \/>ScriptName       : C:\\ps\\t1.ps1<br \/>Line             : {   throw $error<\/p>\n<p>PositionMessage  :<br \/>                   At C:\\ps\\t1.ps1:7 char:10<br \/>                   + {   throw &lt;&lt;&lt;&lt;  $error<\/p>\n<p>InvocationName   : throw<br \/>PipelineLength   : 0<br \/>PipelinePosition : 0<\/p>\n<p>PS&gt;<br \/>\n\t\t<\/span><\/p>\n<p>Enjoy!\n<\/p>\n<p>Jeffrey Snover [MSFT]<br \/>Windows PowerShell\/MMC Architect<br \/>Visit the Windows PowerShell Team blog at:    <a href=\"http:\/\/blogs.msdn.com\/PowerShell\">http:\/\/blogs.msdn.com\/PowerShell<\/a><br \/>Visit the Windows PowerShell ScriptCenter at:  http:\/\/www.microsoft.com\/technet\/scriptcenter\/hubs\/msh.mspx<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Brad Wilson \u2013 the .NET Guy recently posted a set of scripts showing how to drive TortoiseSVN from PowerShell. I thought I would use one of these to highlight how you can leverage the PowerShell semantics to do more with less code. PowerShell is provides Shell semantics and sometimes allows you to ask for more [&hellip;]<\/p>\n","protected":false},"author":600,"featured_media":13641,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-8531","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-powershell"],"acf":[],"blog_post_summary":"<p>Brad Wilson \u2013 the .NET Guy recently posted a set of scripts showing how to drive TortoiseSVN from PowerShell. I thought I would use one of these to highlight how you can leverage the PowerShell semantics to do more with less code. PowerShell is provides Shell semantics and sometimes allows you to ask for more [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts\/8531","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/users\/600"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/comments?post=8531"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts\/8531\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/media\/13641"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/media?parent=8531"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/categories?post=8531"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/tags?post=8531"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}