{"id":33,"date":"2010-02-25T15:00:00","date_gmt":"2010-02-25T22:00:00","guid":{"rendered":"http:\/\/jameskovacs.com\/2010\/02\/25\/The+Exec+Problem"},"modified":"2010-02-25T15:00:00","modified_gmt":"2010-02-25T22:00:00","slug":"the-exec-problem","status":"publish","type":"post","link":"https:\/\/www.jameskovacs.com\/index.php\/2010\/02\/25\/the-exec-problem\/","title":{"rendered":"The Exec Problem"},"content":{"rendered":"<p>I must admit that I don\u2019t much care for PowerShell&#8217;s default behaviour with respect to errors, which is to continue on error. It feels very VB6 \u201cOn Error Resume Next\u201d-ish. Given that it is a shell scripting language, I can understand why the PowerShell team chose this as a default. Fortunately you can change the default by setting $ErrorActionPreference = \u2018Stop\u2019, which terminates execution by throwing an exception. (The default value is Continue, which means the script prints the error and continues executing.) Unfortunately this only works for PowerShell commands and not external executables that return non-zero error codes. (In the shell world, a return code of zero (0) indicates success and anything else indicates failure.)<\/p>\n<p>Take the following simple script:<\/p>\n<pre>'Starting script...'\n$ErrorActionPreference = 'Stop'\nping -badoption\n\"Last Exit Code was: $LastExitCode\"\nrm nonexistent.txt\n'Finished script'<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" style=\"border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px\" title=\"image\" border=\"0\" alt=\"image\" src=\"\/wp-content\/uploads\/WindowsLiveWriter\/TheExecProblem_133DF\/image_eb77e992-14de-41f9-b039-51a1cc609e12.png\" width=\"696\" height=\"456\"> <\/p>\n<p>Notice how execution continued after the ping command failed with an exit code of one (1) even though we have $ErrorActionPreference set to \u2018Stop\u2019. Also notice that the rm command, which is an alias for the PowerShell command, Remove-Item, did cause execution to abort as expected and \u2018Finished script\u2019 was never printed to the console. The discrepancy in error handling between PowerShell commands and executables is annoying and forces us to constantly think about what we\u2019re calling \u2013 a PowerShell command or an executable. The obvious solution is:<\/p>\n<pre>'Starting script...'\n$ErrorActionPreference = 'Stop'\nping -badoption\n<strong><font color=\"#0000ff\">if ($LastExitCode -ne 0) { throw 'An error has occurred...' }<\/font><\/strong>\nrm nonexistent.txt\n'Finished script'<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" style=\"border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px\" title=\"image\" border=\"0\" alt=\"image\" src=\"\/wp-content\/uploads\/WindowsLiveWriter\/TheExecProblem_133DF\/image_431eedb7-21c0-42ce-bedd-19eefe4025a9.png\" width=\"696\" height=\"456\"><\/p>\n<p>The error handling code adds a lot of noise, IMHO, and feels like a throwback to COM and HRESULTs. Can we do better? Jorge Matos, one of the <a href=\"http:\/\/code.google.com\/p\/psake\/\" target=\"_blank\" rel=\"noopener\">psake<\/a> contributors came up with this elegant helper function:<\/p>\n<pre>function Exec([scriptblock]$cmd, [string]$errorMessage = \"Error executing command: \" + $cmd) { \n  &amp; $cmd \n  if ($LastExitCode -ne 0) {\n    throw $errorMessage \n  } \n}<\/pre>\n<p>Note the \u201c&amp; $cmd\u201d syntax. $cmd is a scriptblock and &amp; is used to execute the scriptblock. We can now re-write our original script as follows. (N.B. Exec function is elided for brevity.)<\/p>\n<pre>'Starting script...'\n$ErrorActionPreference = 'Stop'\nexec { ping -badoption }\nrm nonexistent.txt\n'Finished script'<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" style=\"border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px\" title=\"image\" border=\"0\" alt=\"image\" src=\"\/wp-content\/uploads\/WindowsLiveWriter\/TheExecProblem_133DF\/image_528109ea-24da-4bc4-af0b-89a890c10f95.png\" width=\"696\" height=\"456\"><\/p>\n<p>The script now terminates when the bad ping command is executed. We do have to remember to surround executables with exec {}, but this is less noise IMHO than having to check $LastExitCode and throwing an exception.<\/p>\n<p>For those of you using psake for your builds, the Exec helper function is included in the latest versions of the psake module. So you can use it in your build tasks to ensure that you don\u2019t try to run unit tests if msbuild fails horribly. <img decoding=\"async\" alt=\"smile\" src=\"\/wp-includes\/images\/smilies\/icon_smile.gif\"><\/p>\n<p>Happy Scripting!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I must admit that I don\u2019t much care for PowerShell&#8217;s default behaviour with respect to errors, which is to continue on error. It feels very VB6 \u201cOn Error Resume Next\u201d-ish. Given that it is a shell scripting language, I can understand why the PowerShell team chose this as a default. Fortunately you can change the [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[16],"tags":[],"class_list":["post-33","post","type-post","status-publish","format-standard","hentry","category-powershell"],"_links":{"self":[{"href":"https:\/\/www.jameskovacs.com\/index.php\/wp-json\/wp\/v2\/posts\/33","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.jameskovacs.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.jameskovacs.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.jameskovacs.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.jameskovacs.com\/index.php\/wp-json\/wp\/v2\/comments?post=33"}],"version-history":[{"count":0,"href":"https:\/\/www.jameskovacs.com\/index.php\/wp-json\/wp\/v2\/posts\/33\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.jameskovacs.com\/index.php\/wp-json\/wp\/v2\/media?parent=33"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.jameskovacs.com\/index.php\/wp-json\/wp\/v2\/categories?post=33"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.jameskovacs.com\/index.php\/wp-json\/wp\/v2\/tags?post=33"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}