19

I have two Powershell files, a module and a script that calls the module.

Module: test.psm1

Function Get-Info {
    $MyInvocation.MyCommand.Name
}

Script: myTest.ps1

Import-Module C:\Users\moomin\Documents\test.psm1 -force
Get-Info

When I run ./myTest.ps1 I get

Get-Info

I want to return the name of the calling script (test.ps1). How can I do that?

Mark Allison
  • 6,838
  • 33
  • 102
  • 151
  • I realize that this may just be an academic exercise for you, but why not just call `$MyInvocation.MyCommand.Name` from the myTest.ps1 script to get that info? – TheMadTechnician Apr 29 '14 at 16:35
  • It's not an academic exercise. I have posted simple example code to aid the answerer. I need to get the name of the calling script from the module. – Mark Allison Apr 29 '14 at 16:38
  • 1
    possible duplicate of [How can I get the current PowerShell executing file?](http://stackoverflow.com/questions/817198/how-can-i-get-the-current-powershell-executing-file) – JohnC Apr 20 '15 at 01:47
  • @JohnC: This question is about calling script, other question is about executing file. E.g.if main.ps1 calls function from module.psm1, calling script is main.ps1, and executing sctipt is module.psm1 – Michael Freidgeim Jul 14 '17 at 00:59

10 Answers10

20

Use PSCommandPath instead in your module:
Example test.psm1

function Get-Info{
    $MyInvocation.PSCommandPath
}

Example myTest.ps1

Import-Module C:\Users\moomin\Documents\test.psm1 -force
Get-Info

Output:

C:\Users\moomin\Documents\myTest.ps1

If you want only the name of the script that could be managed by doing

GCI $MyInvocation.PSCommandPath | Select -Expand Name

That would output:

myTest.ps1
TheMadTechnician
  • 34,906
  • 3
  • 42
  • 56
  • 1
    Unfortunately, this does work for the PSv3+ [Module Auto-Loading](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_modules?view=powershell-6#module-auto-loading) feature – iRon Aug 19 '18 at 15:48
13

I believe you could use the Get-PSCallStack cmdlet, which returns an array of stack frame objects. You can use this to identify the calling script down to the line of code.

Module: test.psm1

Function Get-Info {
    $callstack = Get-PSCallStack
    $callstack[1].Location
}

Output:

myTest.ps1: Line 2
James
  • 11,721
  • 2
  • 35
  • 41
5

Using the $MyInvocation.MyCommand is relative to it's scope.

A simple example (Of a script located : C:\Dev\Test-Script.ps1):

$name = $MyInvocation.MyCommand.Name;
$path = $MyInvocation.MyCommand.Path;

function Get-Invocation(){
   $path = $MyInvocation.MyCommand.Path;
   $cmd = $MyInvocation.MyCommand.Name; 
   write-host "Command : $cmd - Path : $path";
}

write-host "Command : $cmd - Path : $path";
Get-Invocation;

The output when running .\c:\Dev\Test-Script.ps1 :

Command : C:\Dev\Test-Script.ps1 - Path : C:\Dev\Test-Script.ps1
Command : Get-Invocation - Path : 

As you see, the $MyInvocation is relative to the scoping. If you want the path of your script, do not enclose it in a function. If you want the invocation of the command, then you wrap it.

You could also use the callstack as suggested, but be aware of scoping rules.

Harald F.
  • 4,505
  • 24
  • 29
2

To refer to the invocation info of the calling script, use:

@(Get-PSCallStack)[1].InvocationInfo

e.g.:

@(Get-PSCallStack)[1].InvocationInfo.MyCommand.Name
iRon
  • 20,463
  • 10
  • 53
  • 79
1

I used this today after trying a couple of techniques.

$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$ScriptName = $MyInvocation.MyCommand | select -ExpandProperty Name
Invoke-Expression ". $Script\$ScriptName"
BeastianSTi
  • 515
  • 1
  • 6
  • 8
1

For you googlers looking for quick copy paste solution, here is what works in Powershell 5.1

Inside your module:

$Script = (Get-PSCallStack)[2].Command

This will output just the script name (ScriptName.ps1) which invoked a function located in module.

metablaster
  • 1,958
  • 12
  • 26
1

You can grab the automatic variable MyInvocation from the parent scope and get the name from there.

Get-Variable -Scope:1 -Name:MyInvocation -ValueOnly

I did a basic test to check to see if it would always just get the direct parent scope and it worked like a treat and is extremely fast as opposed to Get-PSCallStack

function ScopeTest () {
    Write-Information -Message:'ScopeTest'
}
Write-nLog -Message:'nLog' -Type:110 -SetLevel:Verbose
ScopeTest

enter image description here

Nick W.
  • 1,536
  • 3
  • 24
  • 40
0

This provides the script path with trailing backslash as one variable and the script name as another.

The path works with Powershell 2.0 and 3.0 and 4.0 and probably 5.0 Where with Posershell $PSscriptroot is now available.

$_INST = $myinvocation.mycommand.path.substring(0,($myinvocation.mycommand.path.length - $MyInvocation.mycommand.name.length))

$_ScriptName = $myinvocation.mycommand.path.substring($MyInvocation.MyCommand.Definition.LastIndexOf('\'),($MyInvocation.mycommand.name.length +1))

$_ScriptName = $_ScriptName.TrimStart('\')

0

If you want a more reusable approach, you can use:

function Get-CallingFileName
{
    $cStack = @(Get-PSCallStack)
    $cStack[$cStack.Length-1].InvocationInfo.MyCommand.Name
}

The challenge I had was having a function that could be reused within the module. Everything else assumed that the script was calling the module function directly and if it was removed even 1 step, then the result would be the module file name. If, however, the source script is calling a function in the module which is, in turn, calling another function in the module, then this is the only answer I've seen that can ensure you're getting the source script info.

Of course, this approach is based on what @iRon and @James posted.

0

I use this in my module:

function Get-ScriptPath {
    [CmdletBinding()]
    param (
        [string]
        $Extension = '.ps1'
    )

    # Allow module to inherit '-Verbose' flag.
    if (($PSCmdlet) -and (-not $PSBoundParameters.ContainsKey('Verbose'))) {
        $VerbosePreference = $PSCmdlet.GetVariableValue('VerbosePreference')
    }

    # Allow module to inherit '-Debug' flag.
    if (($PSCmdlet) -and (-not $PSBoundParameters.ContainsKey('Debug'))) {
        $DebugPreference = $PSCmdlet.GetVariableValue('DebugPreference')
    }
    
    $callstack = Get-PSCallStack

    $i = 0
    $max = 100

    while ($true) {
        if (!$callstack[$i]) {
            Write-Verbose "Cannot detect callstack frame '$i' in 'Get-ScriptPath'."
            return $null
        }

        $path = $callstack[$i].ScriptName

        if ($path) {
            Write-Verbose "Callstack frame '$i': '$path'."
            $ext = [IO.Path]::GetExtension($path)
            if (($ext) -and $ext -eq $Extension) {
                return $path
            }
        }

        $i++

        if ($i -gt $max) {
            Write-Verbose "Exceeded the maximum of '$max' callstack frames in 'Get-ScriptPath'."
            return $null
        }
    }

    return $null
}
Alek Davis
  • 10,628
  • 2
  • 41
  • 53