0
votes

I'm looking for a way to get the path of the main (lets say 'entry point') PowerShell script (*.ps1) file.

Let's for example consider the following example:

  • I have the following files:

"C:\MyDir1\Main.ps1" calls a function in

"C:\MyDir2\SubDir1\MyModule.psm1" which calls a function in

"C:\AnotherDir\AnotherScript.ps1"

  • I run the following PowerShell command:

"PowerShell.exe -File "C:\MyDir1\Main.ps1"

  • What I want: from the script "AnotherScript.ps1", I want to find the path to "C:\MyDir1\Main.ps1".

Note 1: Please note that I'm not talking about the path of the current script which is available via $PSScriptRoot variable.

Note 2: It appears that the variable $global:PSScriptRoot (not the same as $PSScriptRoot) is what I'm looking for but unfortunately, this value is empty when the main script is invoked from a PowerShell session.

1
You could try @(Get-PSCallStack)[1].ScriptName or @(Get-PSCallStack)[1].LocationTheo
I already throught of Get-PSCallStack, it is a possibility, but we have to take the last element or last-1 element according to how the entry point is launched (from a PowerShell session or from powershell.exe -File "Script.ps1" ).Kino101
In that case, wouldn't @(Get-PSCallStack)[-1].ScriptName do the trick?Theo
@(Get-PSCallStack)[-1].ScriptName do not always do the trick, because 'ScriptName' it is null when the script is invoked from a PowerShell prompt (like [PS C:\Temp>. MainScript.ps1])Kino101

1 Answers

0
votes

Here is a function that does the job, but it is very tricky (maybe too much).

function Get-EntryPointAbsFilePath() {

    # NOTE 1: Do not use '$MyInvocation.PSScriptRoot' because it corresponds to the path of the calling script (not entry point script).
    # NOTE 2: '$global:PSScriptRoot' is not the same as '$PSScriptRoot' and seems to correspond to the entry point script directory,
    # but it is set only when the main script is invoked from powershell command like [PowerShell.exe -File "MainScript.ps1"] but not
    # when "MainScript.ps1" is invoked from a PowerShell session (prompt) like [PS C:\Temp>. MainScript.ps1].
    $CallStack = Get-PSCallStack

    # We take the last stack element (correponding to the first call).
    # The 'ScriptName' property of this first call can be null when the main script is invoked from a PowerShell session like [PS C:\Temp>. MainScript.ps1].
    # This is because PowerShell first evaluates the entered command.
    $FirstCall = $CallStack[$CallStack.Count - 1];
    if ($null -ne $FirstCall.ScriptName) {
        return $FirstCall.ScriptName;
    }

    # We take the second call (assuming that we are run under a PowerShell session).
    # To make sure this call is coming from the execution of a script file (and not from the execution a cmdlet in the interpreter, like a function in a module),
    # we check the 'FunctionName' property which equals "<ScriptBlock>" when a call is performed from a script block, like a ps1 file.
    # This test is not required for the first call, as a PowerShell module can't be run.
    $SecondCall = $CallStack[$CallStack.Count - 2];
    if ($null -ne $SecondCall.ScriptName -and $SecondCall.FunctionName -eq "<ScriptBlock>") {
        return $SecondCall.ScriptName;
    }

    throw "No PowerShell entry point script could be found. This cmdlet ""$($MyInvocation.MyCommand.Name)"" is intended to be called only via the execution of a script file.";
}