{"id":880,"date":"2014-08-03T00:01:00","date_gmt":"2014-08-03T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2014\/08\/03\/weekend-scripter-a-hidden-gem-in-the-powershell-ocean-get-pscallstack\/"},"modified":"2014-08-03T00:01:00","modified_gmt":"2014-08-03T00:01:00","slug":"weekend-scripter-a-hidden-gem-in-the-powershell-ocean-get-pscallstack","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/weekend-scripter-a-hidden-gem-in-the-powershell-ocean-get-pscallstack\/","title":{"rendered":"Weekend Scripter: A Hidden Gem in the PowerShell Ocean: Get-PSCallStack"},"content":{"rendered":"<p><b>Summary<\/b>: Microsoft PowerShell MVP, Jeff Wouters, talks about using the <b>Get-PSCallStack<\/b> Windows PowerShell cmdlet.<\/p>\n<p>Microsoft Scripting Guy, Ed Wilson, is here. Today Windows PowerShell MVP, Jeff Wouters, is back with another guest blog post. To read more of Jeff&#039;s previous guest posts, see these <a href=\"https:\/\/devblogs.microsoft.com\/scripting\/tag\/jeff-wouters\/\" target=\"_blank\">Hey, Scripting Guy! Blog posts<\/a>.<\/p>\n<p>And now, Jeff&#8230;<\/p>\n<p>I once tweeted the following:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/wes-8-3-14-1.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/wes-8-3-14-1.png\" alt=\"Image of quote\" title=\"Image of quote\" \/><\/a><\/p>\n<p>This blog post is all about that quote.<\/p>\n<p>Some time ago, I found myself in a situation where I needed to create a generic function that knew what other function or script called it. Let me explain&#8230;<\/p>\n<p>I wanted to write a function to log information to the screen, to a SQL Server database and\/or to a file.<br \/> So if function one calls the logging function, the logging function needed to figure that it was called by function one.<\/p>\n<p>After searching and failing because I was overthinking it, I finally gave up and asked for help from my fellow Windows PowerShell MVPs.<\/p>\n<p>As always, I got a reply. In this case, the reply was from <a href=\"https:\/\/twitter.com\/oising\" target=\"_blank\">Oisin Grehan<\/a>, with exactly the answer I was looking for: <b>Get-PSCallStack<\/b>.<\/p>\n<p>The <b>Get-PSCallStack<\/b> cmdlet gets the current call stack. It was initially written to be used with the Windows PowerShell debugger. However, it can be used outside the debugger to get the call stack inside a script or function. This is exactly what the doctor ordered!<\/p>\n<p>There are many places you can get a call stack, so let&rsquo;s go through them one-by-one.<\/p>\n<h2>A script<\/h2>\n<p>When called from a script, the default output only provides you with the properties <b>Command<\/b>, <b>Arguments<\/b>, and <b>Location<\/b>. However, there are more properties hidden that you can show by using the <b>Select-Object<\/b> cmdlet:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/wes-8-3-14-2.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/wes-8-3-14-2.png\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p>So now let&rsquo;s turn that into a reusable function and look at the call stack again:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/wes-8-3-14-3.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/wes-8-3-14-3.png\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p>That&rsquo;s too much information! I only want the information from the command in the script, and not the function itself.<\/p>\n<p>If the call stack only has a single entry, I want that entry. But if there are two (or more), I want the last entry, right?<\/p>\n<p style=\"margin-left:30px\">function Get-Execution {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $CallStack = Get-PSCallStack | Select-Object -Property *<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; if ($CallStack.Count -eq 1) {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $CallStack[0]<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; } else {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $CallStack[($CallStack.Count &#8211; 1)]<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p><b>Note<\/b>&nbsp; Remember that an array starts counting at 0, so selecting the first object in an array would be <b>$VariableName[0]<\/b>.<\/p>\n<h2>Calling from a function in a script<\/h2>\n<p>The previous code will work when calling the <b>Get-Execution<\/b> function directly from the script. But what if you, like me, prefer to write functions&mdash;so you&rsquo;ll have a function that calls <b>Get-Execution<\/b>. You&rsquo;ll want the entry in the call stack for that function. Let&rsquo;s see what happens:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/wes-8-3-14-4.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/wes-8-3-14-4.png\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p>It works! You can trust me when I write that it keeps working&mdash;no matter how large the tree of functions calling functions&mdash;it keeps working.<\/p>\n<p>But wait, there&rsquo;s more!<\/p>\n<h2>Get-Execution in a module<\/h2>\n<p>Reusable functions are nice, but having to copy them into all my scripts is not something I would prefer. So what if you place <b>Get-Execution<\/b> in a module&mdash;let&rsquo;s say a module with a lot of useful functions that you use in many of your scripts?<br \/> This would negate the need to copy the code into all your scripts. Simply load the module, and all the functions in that mode are available to you. Let&rsquo;s see if it still works:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/wes-8-3-14-5.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/wes-8-3-14-5.png\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p>Yes, it still works!<\/p>\n<p>Now let&rsquo;s try a function in the script calling <b>Get-Execution<\/b> that&rsquo;s inside the module:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/wes-8-3-14-6.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/wes-8-3-14-6.png\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p>Yup, still works like a charm.<\/p>\n<h2>When will it not work?<\/h2>\n<p>This function will not work when calling from a command prompt or in the Windows PowerShell ISE from an unsaved file or selection. In those situations, no call stack is available.<\/p>\n<p>In the case of the command prompt, I&rsquo;ve found a difference in the behavior in Windows PowerShell&nbsp;2.0 versus the later versions.<\/p>\n<p>In Windows PowerShell&nbsp;2.0, no output was given if no call stack was found. Additionally, no error was given. So I was able to check the variable to have no value by comparing to <b>$null<\/b>. In The later versions of Windows PowerShell, I found that this wasn&rsquo;t an option because <b>Get-PSCallStack<\/b> provided output when no call stack was found:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/wes-8-3-14-7.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/wes-8-3-14-7.png\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p>Let&rsquo;s check for an empty call stack, and if no call stack is found, we&#039;ll provide an error message:<\/p>\n<p>That would mean we&rsquo;re done, right?<\/p>\n<p>Wrong&hellip;<\/p>\n<p>When is the default data not enough?<\/p>\n<p>I am actually missing one property in the output (although the data itself is available): The location of the script!<\/p>\n<p>The current output provides the path, but only combined with the name of the script. I want a separate property with the path of the script. Here&rsquo;s the code that also outputs the location of the script by taking it from <b>ScriptName<\/b>:<\/p>\n<p style=\"margin-left:30px\">function Get-Execution {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $CallStack = Get-PSCallStack | Select-Object -Property *<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; if (<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ($CallStack.Count -ne $null) -or<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (($CallStack.Command -ne &#039;&lt;ScriptBlock&gt;&#039;) -and<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ($CallStack.Location -ne &#039;&lt;No file&gt;&#039;) -and<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ($CallStack.ScriptName -ne $Null))<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; ) {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ($CallStack.Count -eq 1) {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $Output = $CallStack[0]<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $Output | Add-Member -MemberType NoteProperty -Name ScriptLocation -Value $((Split-Path $_.ScriptName)[0]) -PassThru<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $Output = $CallStack[($CallStack.Count &#8211; 1)]<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $Output | Add-Member -MemberType NoteProperty -Name ScriptLocation -Value $((Split-Path $Output.ScriptName)[0]) -PassThru<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; } else {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Error -Message &#039;No callstack detected&#039; -Category &#039;InvalidData&#039;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<h2>Use case?<\/h2>\n<p>Why would I want to write such a function?<\/p>\n<p>I like to log the actions of my scripts to a file or to a database&mdash;maybe even to the event log. So when I deliver a script to a customer, I make use of this <b>Get-Execution<\/b> function to mention which file or script did something. Let&rsquo;s take an example&#8230;<\/p>\n<p>I&rsquo;ve got a <b>Write-Log<\/b> function, which combined with <b>Get-Execution<\/b>, can be used like this:<\/p>\n<p style=\"margin-left:30px\">$Execution = Get-Execution<\/p>\n<p style=\"margin-left:30px\">Write-Log -Database &#039;Logging&#039; `<\/p>\n<p style=\"margin-left:30px\">-DBServer &#039;SQLDB01&#039; `<\/p>\n<p style=\"margin-left:30px\">&ndash;Status &lsquo;Error&rsquo; `<\/p>\n<p style=\"margin-left:30px\">-Time ((Get-Date -format o) -replace &#039;:&#039;,&#039;.&#039;) `<\/p>\n<p style=\"margin-left:30px\">-Message &quot;$($Execution.Command) at $($Execution.Location)&quot;<\/p>\n<p>~Jeff<\/p>\n<p>Thanks for sharing, Jeff.<\/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><span style=\"font-size:12px\">&nbsp;<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Microsoft PowerShell MVP, Jeff Wouters, talks about using the Get-PSCallStack Windows PowerShell cmdlet. Microsoft Scripting Guy, Ed Wilson, is here. Today Windows PowerShell MVP, Jeff Wouters, is back with another guest blog post. To read more of Jeff&#039;s previous guest posts, see these Hey, Scripting Guy! Blog posts. And now, Jeff&#8230; I once tweeted [&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,56,381,3,4,61,45],"class_list":["post-880","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-debugging","tag-guest-blogger","tag-jeff-wouters","tag-scripting-guy","tag-scripting-techniques","tag-weekend-scripter","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Microsoft PowerShell MVP, Jeff Wouters, talks about using the Get-PSCallStack Windows PowerShell cmdlet. Microsoft Scripting Guy, Ed Wilson, is here. Today Windows PowerShell MVP, Jeff Wouters, is back with another guest blog post. To read more of Jeff&#039;s previous guest posts, see these Hey, Scripting Guy! Blog posts. And now, Jeff&#8230; I once tweeted [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/880","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=880"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/880\/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=880"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=880"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=880"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}