{"id":4140,"date":"2013-02-19T00:01:00","date_gmt":"2013-02-19T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2013\/02\/19\/use-a-powershell-function-to-see-if-a-command-exists\/"},"modified":"2013-02-19T00:01:00","modified_gmt":"2013-02-19T00:01:00","slug":"use-a-powershell-function-to-see-if-a-command-exists","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/use-a-powershell-function-to-see-if-a-command-exists\/","title":{"rendered":"Use a PowerShell Function to See If a Command Exists"},"content":{"rendered":"<p><strong style=\"font-size: 12px\">Summary:<\/strong><span style=\"font-size: 12px\"> Microsoft Scripting Guy, Ed Wilson, writes a Windows PowerShell function to see if a command exists before calling the command.<\/span><\/p>\n<p>Microsoft Scripting Guy, Ed Wilson, is here. This Thursday, February 21, 2013, I will be on the <a href=\"http:\/\/powerscripting.wordpress.com\/\" target=\"_blank\">PowerScripting Podcast<\/a>. I always enjoy talking with Jon and Hal, and while I am not sure of the actual agenda, I do know that we will be talking about my new Windows PowerShell 3.0 Step by Step book. We might talk about the 2013 Scripting Games, Windows PowerShell Saturday, TechEd, or other things I am doing at the Microsoft TechNet Script Center, and of course, the Hey, Scripting Guy! Blog. Other items related to the Windows PowerShell community will doubtlessly arise. One of the really cool things about the PowerScripting Podcast is the chat room. Lots of cool questions come up via the chat room, and at times, there are even funny comments, and it is all I can do to keep from busting out laughing (in fact, sometimes I do end up laughing &hellip;) It is like a virtual family reunion (actually better than a virtual family reunion because at least we all have something in common&mdash;Windows PowerShell).<\/p>\n<p><strong>Note<\/strong> &nbsp;&nbsp;My <a href=\"http:\/\/en.wikipedia.org\/wiki\/Mentor\" target=\"_blank\">Mentee (or prot&eacute;g&eacute;, or Telemachus)<\/a> Microsoft PFE Ashley McGlone has written an excellent 4-page guide that shows how to use Windows PowerShell to replace RepAdmin, DNSCMD, DCPromo, CSVDE, NETDOM, NLTEST and so on and so on and so on. This is a most excellent reference and is the vision of Windows PowerShell&mdash;replace nearly two dozen command-line utilities, each with its own confusing and different syntax, with the consistency of Windows PowerShell. It is way cool and should really help to simplify your life. <a href=\"http:\/\/blogs.technet.com\/b\/ashleymcglone\/archive\/2013\/01\/02\/free-download-cmd-to-powershell-guide-for-ad.aspx\" target=\"_blank\">You can download it from his blog here<\/a>.<\/p>\n<h2>Finding required commands in Windows PowerShell<\/h2>\n<p>In a recent PowerTip, I talked about using the <strong>Get-ADReplicationConnection<\/strong> cmdlet to find information about AD DS replication. At the time, I wanted to confirm if the cmdlet was available by determining which version of the ActiveDirectory module is installed. Unfortunately, as I checked around, the only way I had to confirm this was to go to a computer with the specific version of the ActiveDirectory module. I had hoped I could check the <strong>version<\/strong><em> <\/em>property of the module, but unfortunately, the AD DS team did not increment the version number of the module. This is shown here:<\/p>\n<p style=\"padding-left: 30px\">PS C:\\&gt; ipmo activedirectory<\/p>\n<p style=\"padding-left: 30px\">PS C:\\&gt; (Get-Module activedirectory).version<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">Major&nbsp; Minor&nbsp; Build&nbsp; Revision<\/p>\n<p style=\"padding-left: 30px\">&#8212;&#8211;&nbsp; &#8212;&#8211;&nbsp; &#8212;&#8211;&nbsp; &#8212;&#8212;&#8211;<\/p>\n<p style=\"padding-left: 30px\">1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0<\/p>\n<p>This leaves me with the situation in which if I want to see if a cmdlet (or a function) exists, I can attempt to use it, and generate an error if the command does not exist. Or I can use the <strong>Get-Command<\/strong> cmdlet to search for the cmdlet (or function), but once again, it generates an error if the command does not exist.<\/p>\n<p>So, I decided to write my own function to see if a command exists.<\/p>\n<h2>The Test-CommandExists function<\/h2>\n<p>To keep from generating an error when I call the <strong>Get-Command<\/strong> cmdlet, I use Try \/ Catch \/ Finally. There is one problem, and that is that a command not found is not a terminating error, and Try \/ Catch \/ Finally only handles terminating errors. Terminating errors are errors that are so bad that Windows PowerShell cannot do anything but halt executing. A non-terminating error is bad, but Windows PowerShell will, by default, continue to the next command. To change this behavior, I need to change the <strong>$ErrorActionPreference<\/strong> value from the default value of <strong>continue<\/strong><em> <\/em>to <strong>stop<\/strong><em>. <\/em>In this way, the non-terminating error will cause Windows PowerShell to halt execution, therefore permitting the Try \/ Catch \/ Finally block to handle the error condition.<\/p>\n<p>But, changing a preference variable without asking is just rude (and is not considered to be a best practice), so, I am going to only change the preference variable while my function runs, and I will change it back when I am done. This is the great thing about Try \/ Catch \/ Finally&mdash;that I can specify code for each step of the process.<\/p>\n<p style=\"padding-left: 30px\"><strong>Note<\/strong> &nbsp;&nbsp;The Finally ALWAYS runs, and it is a perfect place to set things back in their proper condition. I always put my <em>cleanup <\/em>code in the Finally block.<\/p>\n<p>Therefore, one of the first things I do in my function is store the current <strong>$ErrorActionPreference<\/strong> value and then I assign the value of <strong>stop<\/strong> to <strong>$ErrorActionPreference<\/strong>. These two lines of code are shown here.<\/p>\n<p style=\"padding-left: 30px\">$oldPreference = $ErrorActionPreference<\/p>\n<p style=\"padding-left: 30px\">$ErrorActionPreference = &#8216;stop&#8217;<\/p>\n<p>I create an input parameter that permits me to specify the command I am interested in when I call the function. This line is shown here.<\/p>\n<p style=\"padding-left: 30px\">Param ($command)<\/p>\n<p>I use <strong>Try<\/strong> to see if the command exists. To do this, I use the <strong>If<\/strong> language statement, and I evaluate the condition occurring when I use <strong>Get-Command<\/strong> to look for the command. If the command exists, I display this information. The line of code is shown here.<\/p>\n<p style=\"padding-left: 30px\">try {if(Get-Command $command){&#8220;$command exists&#8221;}}&nbsp;<\/p>\n<p>Now I use <strong>Catch<\/strong> to catch the error condition. Therefore, if <strong>Get-Command $command<\/strong> results in an error, the code progresses to <strong>Catch<\/strong>, and I display a message stating the command does not exist. This is shown here.<\/p>\n<p style=\"padding-left: 30px\">Catch {&#8220;$command does not exist&#8221;}<\/p>\n<p>If an error occurs, the <strong>Catch<\/strong> statement displays a message. The code continues to the <strong>Finally<\/strong> block. But if an error does not occur, the <strong>Catch<\/strong> statement does not execute. However, the <strong>Finally<\/strong> block still executes because FINALLY ALWAYS runs. In the Script Block associated with <strong>Finally<\/strong>, I set <strong>$ErrorActionPreference<\/strong> back to the original value&mdash;the value I stored when the function began. This code is shown here.<\/p>\n<p style=\"padding-left: 30px\">Finally {$ErrorActionPreference=$oldPreference}<\/p>\n<p>The complete <strong>Test-CommandExists<\/strong> function is shown here.<\/p>\n<p style=\"padding-left: 30px\">Function Test-CommandExists<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Param ($command)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;$oldPreference = $ErrorActionPreference<\/p>\n<p style=\"padding-left: 30px\">&nbsp;$ErrorActionPreference = &#8216;stop&#8217;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;try {if(Get-Command $command){&#8220;$command exists&#8221;}}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Catch {&#8220;$command does not exist&#8221;}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Finally {$ErrorActionPreference=$oldPreference}<\/p>\n<p style=\"padding-left: 30px\">} #end function test-CommandExists<\/p>\n<p>When I run the function, the following output is shown.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4628.HSG-2-19-13-01.png\"><img decoding=\"async\" title=\"Image of function output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4628.HSG-2-19-13-01.png\" alt=\"Image of function output\" \/><\/a><\/p>\n<h2>Making the Test-CommandExists function more friendly<\/h2>\n<p>If I configure the function to return Boolean values, instead of strings, I can use the <strong>If<\/strong> statement when calling the code. Here is the revision to the function:<\/p>\n<p style=\"padding-left: 30px\">Function Test-CommandExists<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Param ($command)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;$oldPreference = $ErrorActionPreference<\/p>\n<p style=\"padding-left: 30px\">&nbsp;$ErrorActionPreference = &#8216;stop&#8217;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;try {if(Get-Command $command){RETURN $true}}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Catch {Write-Host &#8220;$command does not exist&#8221;; RETURN $false}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Finally {$ErrorActionPreference=$oldPreference}<\/p>\n<p style=\"padding-left: 30px\">} #end function test-CommandExists<\/p>\n<p>I do not need to add the RETURN key word, but it makes the code easier to read. The <strong>Try<\/strong> section is basic. It is the <strong>Catch<\/strong> line where I had to get creative. If I have the <strong>Catch<\/strong> section return <strong>$false<\/strong>, nothing displays when I call the function as seen here.<\/p>\n<p style=\"padding-left: 30px\">If(Test-CommandExists Get-myService){Get-myService}<\/p>\n<p>But, on the other hand, if I have a string that states the command does not exist, it is evaluated as <strong>$true<\/strong> and the code attempts to run, and an error generates. Therefore, I decided to use <strong>Write-Host<\/strong> here because it does not return to the output stream, and therefore, the calling code does not see it. The use of this function is shown here.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/3660.HSG-2-19-13-02.png\"><img decoding=\"async\" title=\"Image of function output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/3660.HSG-2-19-13-02.png\" alt=\"Image of function output\" \/><\/a><\/p>\n<p>Join me tomorrow when I will talk about more cool Windows PowerShell 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><strong>Ed Wilson, Microsoft Scripting Guy<\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Microsoft Scripting Guy, Ed Wilson, writes a Windows PowerShell function to see if a command exists before calling the command. Microsoft Scripting Guy, Ed Wilson, is here. This Thursday, February 21, 2013, I will be on the PowerScripting Podcast. I always enjoy talking with Jon and Hal, and while I am not sure of [&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":[3,4,45,77],"class_list":["post-4140","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-scripting-guy","tag-scripting-techniques","tag-windows-powershell","tag-writing"],"acf":[],"blog_post_summary":"<p>Summary: Microsoft Scripting Guy, Ed Wilson, writes a Windows PowerShell function to see if a command exists before calling the command. Microsoft Scripting Guy, Ed Wilson, is here. This Thursday, February 21, 2013, I will be on the PowerScripting Podcast. I always enjoy talking with Jon and Hal, and while I am not sure of [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/4140","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=4140"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/4140\/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=4140"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=4140"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=4140"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}