I wrote a couple recent posts that used the Get-PSCallStack cmdlet. In my case, Get-PSCallStack helped one function determine what function invoked it, and used the information to further determine if that function was approved to invoke it, or not. In doing this, I wrote a series of small functions I’d like to share to help demonstrate this process.
In the below code example, I’ve defined a series of functions. The first function defined is called Get-Main, and it does a few things. It sets a variable $Index to zero, dumps the Get-PSCallStack commands into a global variable (that will exist after the function completes), and then loops through each of the command properties in the second variable, displaying its correctly assumed index, and the command name.
The following functions are in place to progressively call one another from the bottom up. Working downward, however, Get-OneUpOne invokes Get-Main (discussed above), Get-OneUpTwo invokes Get-OneUpOne, Get-OneUpThree invokes Get-OneUpTwo, etc. These multiple functions end up being listed in Get-PSCallStack, as we’ll soon see.
Function Get-Main { $Index = 0 $Global:Var = (Get-PSCallStack).Command $Var | ForEach-Object { "Index $Index : $_" $Index ++ } } Function Get-OneUpOne { Get-Main } Function Get-OneUpTwo { Get-OneUpOne } Function Get-OneUpThree { Get-OneUpTwo } Function Get-OneUpFour { Get-OneUpThree } Get-OneUpFour
The last line in the above example, starts the magic by invoking Get-OneUpFour, that invokes Get-OneUpThree, etc., moving upward. Here’s a gif that shows how these are invoked, working up from the bottom.
When this chunk of code is dropped into the ISE and run, we get the results listed below. They indicate the index and value of Get-PSCallStack’s recorded commands, or steps, on the way to invoking the Get-Main function. Notice, that it’s backwards, and what I mean is this: The first index 0, is the last command recorded. The last index, 5, is the first command recorded.
Index 0 : Get-Main Index 1 : Get-OneUpOne Index 2 : Get-OneUpTwo Index 3 : Get-OneUpThree Index 4 : Get-OneUpFour Index 5 : <ScriptBlock>
Since we created the global variable $Var, we can verify if the above results are correct — if our assumed index is accurate. We’ll check with a single index first, and then run $Var though a For loop to see each index.
$Var[2] Get-OneUpTwo
For ($i = 0; $i -lt $Var.Length; $i++) { "Index $i -- $($Var[$i])" } Index 0 -- Get-Main Index 1 -- Get-OneUpOne Index 2 -- Get-OneUpTwo Index 3 -- Get-OneUpThree Index 4 -- Get-OneUpFour Index 5 -- Get-PSCallStack_Example.ps1
Take a look at index 5 in the directly above example, and then the further above example. One says, “<ScriptBlock>,” and one says, “Get-PSCallStack_Example.ps1.” Any idea what changed between these two examples? If you guessed that I saved my code example, while writing this post, then you’d be correct.
The highest numbered index* — 5 in this example — is always going to be the starting point — whether it’s called from the prompt, or a saved file, and the lowest index — index 0 — will always be the currently invoked function. This means that index 1 will always be the calling function, of the last function that was invoked.
* The highest numbered index will also always be index -1, such as $Var[-1] would equal “<ScriptBlock>,” or “Get-PSCallStack_Example.ps1.” Knowing this, it’s possible to work backwards through these results, or any other array.
So it’s been said, the other time we see an array in reverse, is the $Error variable. $Error[0] is always the most recent error, not the first error. Thanks for reading, and we’ll do it again sometime soon.