123

Note: PowerShell 1.0
I'd like to get the current executing PowerShell file name. That is, if I start my session like this:

powershell.exe .\myfile.ps1

I'd like to get the string ".\myfile.ps1" (or something like that). EDIT: "myfile.ps1" is preferable.
Any ideas?

Ron Klein
  • 9,178
  • 9
  • 55
  • 88
  • Thanks, current answers are almost the same, but I only need the file name (and not the whole path), so the accepted answer is @Keith's. +1 to both answers, though. Now I know about the $MyInvocation thingy :-) – Ron Klein May 03 '09 at 21:54
  • How about getting the parent script from an included script? – Florin Sabau Apr 25 '13 at 17:06

12 Answers12

116

I've tried to summarize the various answers here, updated for PowerShell 5:

  • If you're only using PowerShell 3 or higher, use $PSCommandPath

  • If want compatibility with older versions, insert the shim:

    if ($PSCommandPath -eq $null) { function GetPSCommandPath() { return $MyInvocation.PSCommandPath; } $PSCommandPath = GetPSCommandPath }

    This adds $PSCommandPath if it doesn't already exist.

    The shim code can be executed anywhere (top-level or inside a function), though $PSCommandPath variable is subject to normal scoping rules (eg, if you put the shim in a function, the variable is scoped to that function only).

Details

There's 4 different methods used in various answers, so I wrote this script to demonstrate each (plus $PSCommandPath):

function PSCommandPath() { return $PSCommandPath }
function ScriptName() { return $MyInvocation.ScriptName }
function MyCommandName() { return $MyInvocation.MyCommand.Name }
function MyCommandDefinition() {
    # Begin of MyCommandDefinition()
    # Note: ouput of this script shows the contents of this function, not the execution result
    return $MyInvocation.MyCommand.Definition
    # End of MyCommandDefinition()
}
function MyInvocationPSCommandPath() { return $MyInvocation.PSCommandPath }

Write-Host ""
Write-Host "PSVersion: $($PSVersionTable.PSVersion)"
Write-Host ""
Write-Host "`$PSCommandPath:"
Write-Host " *   Direct: $PSCommandPath"
Write-Host " * Function: $(PSCommandPath)"
Write-Host ""
Write-Host "`$MyInvocation.ScriptName:"
Write-Host " *   Direct: $($MyInvocation.ScriptName)"
Write-Host " * Function: $(ScriptName)"
Write-Host ""
Write-Host "`$MyInvocation.MyCommand.Name:"
Write-Host " *   Direct: $($MyInvocation.MyCommand.Name)"
Write-Host " * Function: $(MyCommandName)"
Write-Host ""
Write-Host "`$MyInvocation.MyCommand.Definition:"
Write-Host " *   Direct: $($MyInvocation.MyCommand.Definition)"
Write-Host " * Function: $(MyCommandDefinition)"
Write-Host ""
Write-Host "`$MyInvocation.PSCommandPath:"
Write-Host " *   Direct: $($MyInvocation.PSCommandPath)"
Write-Host " * Function: $(MyInvocationPSCommandPath)"
Write-Host ""

Output:

PS C:\> .\Test\test.ps1

PSVersion: 5.1.19035.1

$PSCommandPath:
 *   Direct: C:\Test\test.ps1
 * Function: C:\Test\test.ps1

$MyInvocation.ScriptName:
 *   Direct:
 * Function: C:\Test\test.ps1

$MyInvocation.MyCommand.Name:
 *   Direct: test.ps1
 * Function: MyCommandName

$MyInvocation.MyCommand.Definition:
 *   Direct: C:\Test\test.ps1
 * Function:
    # Begin of MyCommandDefinition()
    # Note this is the contents of the MyCommandDefinition() function, not the execution results
    return $MyInvocation.MyCommand.Definition;
    # End of MyCommandDefinition()


$MyInvocation.PSCommandPath:
 *   Direct:
 * Function: C:\Test\test.ps1

Notes:

  • Executed from C:\, but actual script is C:\Test\test.ps1.
  • No method tells you the passed invocation path (.\Test\test.ps1)
  • $PSCommandPath is the only reliable way, but was introduced in PowerShell 3
  • For versions prior to 3, no single method works both inside and outside of a function
iRon
  • 20,463
  • 10
  • 53
  • 79
gregmac
  • 24,276
  • 10
  • 87
  • 118
  • 8
    For anyone reading today (2017), they should be reading THIS post as the correct answer! +1 – Collin Chaffin Jul 25 '17 at 13:59
  • 2
    @CollinChaffin: agreed and now (2017) the least currently supported is Windows 7 so there is no reason not to use `$PSCommandPath` if legacy (WindowsXP) is not required. – tukan Dec 11 '17 at 16:30
  • The first code example is flawed as it contains two definitions of the same function (`function PSCommandPath` ) and a reference to the wrong function ( `Write-Host " * Direct: $PSCommandPath"; Write-Host " * Function: $(ScriptName)";` - or am I overlooking something obvious ? – Mike L'Angelo Aug 19 '20 at 17:42
  • @MikeL'Angelo You're right! Went unnoticed for 3 years. Fixed, thank you. The output and conclusion is the same though. – gregmac Aug 20 '20 at 14:48
  • 1
    A long answer where `$PSCommandPath` suffices in 99% of cases. Thanks though. – Timo May 13 '21 at 10:47
  • I haven't got a PS <3 at hand, but woudn't it be enough simply to fallback to `if ($PSCommandPath -eq $null) { $PSCommandPath = $MyInvocation.PSCommandPath; }`? – Nuno André Dec 22 '21 at 21:10
86

While the current Answer is right in most cases, there are certain situations that it will not give you the correct answer. If you use inside your script functions then:

$MyInvocation.MyCommand.Name 

Returns the name of the function instead name of the name of the script.

function test {
    $MyInvocation.MyCommand.Name
}

Will give you "test" no matter how your script is named. The right command for getting the script name is always

$MyInvocation.ScriptName

this returns the full path of the script you are executing. If you need just the script filename than this code should help you:

split-path $MyInvocation.PSCommandPath -Leaf
rory.ap
  • 34,009
  • 10
  • 83
  • 174
Lukas Kucera
  • 895
  • 7
  • 3
  • 6
    Note that at the top level, Scriptname is undefined with posh v4. I like to use at the top level, $MyInvocation.MyCommand.Definition for full path or Name as per the other answers. – AnneTheAgile Feb 21 '15 at 23:00
  • 30
    `$MyInvocation.ScriptName` return empty string for me, PS v3.0. – JohnC Apr 20 '15 at 01:42
  • 4
    @JohnC `$MyInvocation.ScriptName` only works from inside a function. See [my answer below](http://stackoverflow.com/a/43643346/7913). – gregmac Apr 26 '17 at 19:52
76

If you only want the filename (not the full path) use this:

$ScriptName = $MyInvocation.MyCommand.Name
Keith Hill
  • 2,329
  • 1
  • 19
  • 7
  • That's false for any use outside the global scope. If you create a function and you invoke this code in a function, you will get the name of the function rather then the name of the file. – ashrasmun Dec 23 '21 at 17:50
34

Try the following

$path =  $MyInvocation.MyCommand.Definition 

This may not give you the actual path typed in but it will give you a valid path to the file.

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
9

beware: Unlike the $PSScriptRoot and $PSCommandPath automatic variables, the PSScriptRoot and PSCommandPath properties of the $MyInvocation automatic variable contain information about the invoker or calling script, not the current script.

e.g.

PS C:\Users\S_ms\OneDrive\Documents> C:\Users\SP_ms\OneDrive\Documents\DPM ...
=!C:\Users\S_ms\OneDrive\Documents\DPM.ps1

...where DPM.ps1 contains

Write-Host ("="+($MyInvocation.PSCommandPath)+"!"+$PSCommandPath)
chiwangc
  • 3,566
  • 16
  • 26
  • 32
MWR
  • 91
  • 1
  • 1
8

If you are looking for the current directory in which the script is being executed, you can try this one:

$fullPathIncFileName = $MyInvocation.MyCommand.Definition
$currentScriptName = $MyInvocation.MyCommand.Name
$currentExecutingPath = $fullPathIncFileName.Replace($currentScriptName, "")

Write-Host $currentExecutingPath
Ryk
  • 3,072
  • 5
  • 27
  • 32
  • 1
    That wouldn't work correctly on `C:\ilike.ps123\ke.ps1`, would it? – fridojet Jun 06 '12 at 19:48
  • @fridojet - Not sure, not near a PS terminal to test it. Why dont you try it and see? – Ryk Jun 06 '12 at 23:00
  • No, just a rhetorical question ;-) - It would be just logical because the `Replace()` method replaces **every** occurrence of the needle (not just the last occurrence) and I also tested it. However, it's a nice idea to do something like subtraction on strings. – fridojet Jun 07 '12 at 18:19
  • ... What about `String.TrimEnd()` (`$currentExecutingPath = $fullPathIncFileName.TrimEnd($currentScriptName)`)? - It's working correctly: `"Ich bin Hamster".TrimEnd("ster")` returns `Ich bin Ham` and `"Ich bin Hamsterchen".TrimEnd("ster")` returns `Ich bin Hamsterchen` (instead of `Ich bin Hamchen`) - Fine! – fridojet Jun 07 '12 at 19:24
  • `$currentScriptPath = $MyInvocation.MyCommand.Definition; $currentScriptName = $MyInvocation.MyCommand.Name; $currentScriptDir = $currentScriptPath.Substring(0,$currentScriptPath.IndexOf($currentScriptName));` – Y P Nov 25 '13 at 09:48
  • Or simply `[System.IO.Path]::GetDirectoryName($MyInvocation.MyCommand.Definition)` – Jozef Benikovský Oct 19 '16 at 12:35
7

As noted in previous responses, using "$MyInvocation" is subject to scoping issues and doesn't necessarily provide consistent data (return value vs. direct access value). I've found that the "cleanest" (most consistent) method for getting script info like script path, name, parms, command line, etc. regardless of scope (in main or subsequent/nested function calls) is to use "Get-Variable" on "MyInvocation"...

# Get the MyInvocation variable at script level
# Can be done anywhere within a script
$ScriptInvocation = (Get-Variable MyInvocation -Scope Script).Value

# Get the full path to the script
$ScriptPath = $ScriptInvocation.MyCommand.Path

# Get the directory of the script
$ScriptDirectory = Split-Path $ScriptPath

# Get the script name
# Yes, could get via Split-Path, but this is "simpler" since this is the default return value
$ScriptName = $ScriptInvocation.MyCommand.Name

# Get the invocation path (relative to $PWD)
# @GregMac, this addresses your second point
$InvocationPath = ScriptInvocation.InvocationName

So, you can get the same info as $PSCommandPath, but a whole lot more in the deal. Not sure, but it looks like "Get-Variable" was not available until PS3 so not a lot of help for really old (not updated) systems.

There are also some interesting aspects when using "-Scope" as you can backtrack to get the names, etc. of the calling function(s). 0=current, 1=parent, etc.

Hope this is somewhat helpful.

Ref, https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/get-variable

AntGut
  • 71
  • 1
  • 4
5

I would argue that there is a better method, by setting the scope of the variable $MyInvocation.MyCommand.Path:

ex> $script:MyInvocation.MyCommand.Name

This method works in all circumstances of invocation:

EX: Somescript.ps1

function printme () {
    "In function:"
    ( "MyInvocation.ScriptName: " + [string]($MyInvocation.ScriptName) )
    ( "script:MyInvocation.MyCommand.Name: " + [string]($script:MyInvocation.MyCommand.Name) )
    ( "MyInvocation.MyCommand.Name: " + [string]($MyInvocation.MyCommand.Name) )
}
"Main:"
( "MyInvocation.ScriptName: " + [string]($MyInvocation.ScriptName) )
( "script:MyInvocation.MyCommand.Name: " + [string]($script:MyInvocation.MyCommand.Name) )
( "MyInvocation.MyCommand.Name: " + [string]($MyInvocation.MyCommand.Name) )
" "
printme
exit

OUTPUT:

PS> powershell C:\temp\test.ps1
Main:
MyInvocation.ScriptName:
script:MyInvocation.MyCommand.Name: test.ps1
MyInvocation.MyCommand.Name: test.ps1

In function:
MyInvocation.ScriptName: C:\temp\test.ps1
script:MyInvocation.MyCommand.Name: test.ps1
MyInvocation.MyCommand.Name: printme

Notice how the above accepted answer does NOT return a value when called from Main. Also, note that the above accepted answer returns the full path when the question requested the script name only. The scoped variable works in all places.

Also, if you did want the full path, then you would just call:

$script:MyInvocation.MyCommand.Path
Daddio
  • 51
  • 1
  • 3
4

A short demonstration of @gregmac's (excellent and detailed) answer, which essentially recommends $PSCommandPath as the only reliable command to return the currently running script where Powershell 3.0 and above is used.

Here I show returning either the full path or just the file name.

Test.ps1:

'Direct:'
$PSCommandPath  # Full Path 
Split-Path -Path $PSCommandPath -Leaf  # File Name only

function main () {
  ''
  'Within a function:'
  $PSCommandPath
  Split-Path -Path $PSCommandPath -Leaf
}

main

Output:

PS> .\Test.ps1
Direct:
C:\Users\John\Documents\Sda\Code\Windows\PowerShell\Apps\xBankStatementRename\Test.ps1
Test.ps1

Within a function:
C:\Users\John\Documents\Sda\Code\Windows\PowerShell\Apps\xBankStatementRename\Test.ps1
Test.ps1
John Bentley
  • 1,676
  • 1
  • 16
  • 18
2

Did some testing with the following script, on both PS 2 and PS 4 and had the same result. I hope this helps people.

$PSVersionTable.PSVersion
function PSscript {
  $PSscript = Get-Item $MyInvocation.ScriptName
  Return $PSscript
}
""
$PSscript = PSscript
$PSscript.FullName
$PSscript.Name
$PSscript.BaseName
$PSscript.Extension
$PSscript.DirectoryName

""
$PSscript = Get-Item $MyInvocation.InvocationName
$PSscript.FullName
$PSscript.Name
$PSscript.BaseName
$PSscript.Extension
$PSscript.DirectoryName

Results -

Major  Minor  Build  Revision
-----  -----  -----  --------
4      0      -1     -1      

C:\PSscripts\Untitled1.ps1
Untitled1.ps1
Untitled1
.ps1
C:\PSscripts

C:\PSscripts\Untitled1.ps1
Untitled1.ps1
Untitled1
.ps1
C:\PSscripts
1

This can works on most powershell versions:

(& { $MyInvocation.ScriptName; })

This can work for Scheduled Job

Get-ScheduledJob |? Name -Match 'JOBNAMETAG' |% Command
unlikely
  • 398
  • 2
  • 8
0

+1 for @AntGut's solution. I use included common scripts and MyInvocation and PSCommandPath are scoped to the included script.

I have a common logging function and to get the name of the top-level script, this works:

$ScriptInvocation = (Get-Variable MyInvocation -Scope Script).Value
$ScriptName = $ScriptInvocation.MyCommand.Name