Mark Allison
Mark Allison

Reputation: 7228

How to return the name of the calling script from a Powershell Module?

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?

Upvotes: 20

Views: 26882

Answers (10)

Nick W.
Nick W.

Reputation: 1614

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

Upvotes: 1

Alek Davis
Alek Davis

Reputation: 10742

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
}

Upvotes: 0

metablaster
metablaster

Reputation: 2184

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.

Upvotes: 1

Josh Bailey
Josh Bailey

Reputation: 1

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.

Upvotes: 0

iRon
iRon

Reputation: 23693

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

@(Get-PSCallStack)[1].InvocationInfo

e.g.:

@(Get-PSCallStack)[1].InvocationInfo.MyCommand.Name

Upvotes: 2

DeployGuy
DeployGuy

Reputation: 1

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('\')

Upvotes: 0

BeastianSTi
BeastianSTi

Reputation: 605

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"

Upvotes: 1

Harald F.
Harald F.

Reputation: 4773

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.

Upvotes: 5

James
James

Reputation: 11931

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

Upvotes: 13

TheMadTechnician
TheMadTechnician

Reputation: 36297

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

Upvotes: 20

Related Questions