260

This should be a simple task, but I have seen several attempts on how to get the path to the directory where the executed cmdlet is located with mixed success. For instance, when I execute C:\temp\myscripts\mycmdlet.ps1 which has a settings file at C:\temp\myscripts\settings.xml I would like to be able to store C:\temp\myscripts in a variable within mycmdlet.ps1.

This is one solution which works (although a bit cumbersome):

$invocation = (Get-Variable MyInvocation).Value
$directorypath = Split-Path $invocation.MyCommand.Path
$settingspath = $directorypath + '\settings.xml'

Another one suggested this solution which only works on our test environment:

$settingspath = '.\settings.xml'

I like the latter approach a lot and prefer it to having to parse the filepath as a parameter each time, but I can't get it to work on my development environment. What should I do? Does it have something to do with how PowerShell is configured?

Stig Perez
  • 3,595
  • 4
  • 23
  • 36
  • 13
    Note that the ambiguous title of this question has resulted in the answers below solving one of two distinct problems, without explicitly stating which: (a) how to reference the _current_ location (directory) _or_ (b) how to reference the _running script's_ location (the directory in which the running script is located, which may or may not be the current directory). – mklement0 Jan 24 '18 at 04:21
  • 1
    Possible duplicate of [What's the best way to determine the location of the current PowerShell script?](https://stackoverflow.com/questions/5466329/whats-the-best-way-to-determine-the-location-of-the-current-powershell-script) – jpmc26 Apr 13 '18 at 21:05

20 Answers20

303

Yes, that should work. But if you need to see the absolute path, this is all you need:

(Get-Item .).FullName
avi12
  • 2,000
  • 5
  • 24
  • 41
GuruKay
  • 3,429
  • 2
  • 20
  • 8
  • 4
    Thanks, this is a great method to find the full path from relative paths. E.g. (Get-Item -Path $myRelativePath -Verbose).FullName – dlux May 06 '14 at 03:51
  • Thank you for this. Other answers weren't working for Powershell scripts compiled to EXEs. – Zach Alexander Nov 03 '17 at 14:40
  • 21
    This is *wrong*. This gets the current directory of the *process*, which can be anywhere. For example, if my command line current directory is `C:\mydir`, and I invoke the command `C:\dir1\dir2\dir3\mycmdlet.ps1`, then this will resolve to `C:\mydir`, not `C:\dir1\dir2\dir3`. Invoking a new executable has the same problem since the current directory is inherited from the parent process. – jpmc26 Apr 13 '18 at 21:00
  • Worked like a charm – Aquaphor May 24 '21 at 21:25
195

The reliable way to do this is just like you showed $MyInvocation.MyCommand.Path.

Using relative paths will be based on $pwd, in PowerShell, the current directory for an application, or the current working directory for a .NET API.

PowerShell v3+:

Use the automatic variable $PSScriptRoot.

JasonMArcher
  • 14,195
  • 22
  • 56
  • 52
  • 6
    Can you please explain me how you found property PATH? $MyInvocation.MyCommand|gm does not show such property in members list. – Vitaliy Markitanov Dec 11 '16 at 15:57
  • 24
    why not just use $PSScriptRoot? Seems more reliable – mBrice1024 Jul 27 '17 at 21:33
  • @user2326106 Can you explain the difference between `$PSScriptRoot` and `$MyInvocation.MyCommand.Path`? – duct_tape_coder Jun 14 '19 at 20:53
  • 1
    @VitaliyMarkitanov Did you run `$MyInvocation.MyCommand | gm` within a script? `Path` refers to the full path of the script file, so you won't find it by executing in the terminal directly. – Connor Low Apr 29 '20 at 15:39
  • I was having trouble with `$MyInvocation.MyCommand.Path\File.csv` & after I googled `$MyInvocation.MyCommand.Path` I found this [MS article](https://devblogs.microsoft.com/powershell/how-can-a-script-tell-what-directory-it-was-run-from/) detailing I had to split some of it off & put in variable before I could get it to work: `$myDir = Split-Path -Parent $MyInvocation.MyCommand.Path` . VERY new to powershell so problem is likely my lack of understanding – gregg Oct 19 '20 at 21:23
96

The easiest method seems to be to use the following predefined variable:

 $PSScriptRoot

about_Automatic_Variables and about_Scripts both state:

In PowerShell 2.0, this variable is valid only in script modules (.psm1). Beginning in PowerShell 3.0, it is valid in all scripts.

I use it like this:

 $MyFileName = "data.txt"
 $filebase = Join-Path $PSScriptRoot $MyFileName
Lance U. Matthews
  • 15,725
  • 6
  • 48
  • 68
Tony Sheen
  • 961
  • 6
  • 2
  • 13
    It is version specific. This requires at least Powershell 3.0. – Marvin Dickhaus Apr 10 '15 at 13:46
  • 1
    This is what I needed to reference a file in the same location as the script--thanks! – Adam Prescott Nov 21 '17 at 14:51
  • This is the best answer as it gives you precisely the path where you PS script is present which is essentially the root for your script execution. It doesn't care what is your present working directory from where you've invoked your scripts. +1. – RBT May 17 '18 at 00:31
  • @MarvinDickhaus That's why it's needed to use "Set-StrictMode -Version 3.0" in most your scripts :) Big thank's for the links! – Alexander Shapkin May 28 '18 at 13:08
47

You can also use:

(Resolve-Path .\).Path

The part in brackets returns a PathInfo object.

(Available since PowerShell 2.0.)

Alex Angas
  • 59,219
  • 41
  • 137
  • 210
  • 4
    This is *wrong*. This gets the current directory of the *process*, which can be anywhere. For example, if my command line current directory is `C:\mydir`, and I invoke the command `C:\dir1\dir2\dir3\mycmdlet.ps1`, then this will resolve to `C:\mydir`, not `C:\dir1\dir2\dir3`. Invoking a new executable has the same problem since the current directory is inherited from the parent process. – jpmc26 Apr 13 '18 at 21:04
  • 4
    Thanks! I also misunderstood the title of this question, and this answer was exactly what I was looking for. However... It doesn't answer the question. – Ryan Leach Jun 28 '18 at 07:41
47

Try :

(Get-Location).path

or:

($pwd).path
Nae
  • 14,209
  • 7
  • 52
  • 79
Rohin Sidharth
  • 1,095
  • 1
  • 10
  • 9
33

Path is often null. This function is safer.

function Get-ScriptDirectory
{
    $Invocation = (Get-Variable MyInvocation -Scope 1).Value;
    if($Invocation.PSScriptRoot)
    {
        $Invocation.PSScriptRoot;
    }
    Elseif($Invocation.MyCommand.Path)
    {
        Split-Path $Invocation.MyCommand.Path
    }
    else
    {
        $Invocation.InvocationName.Substring(0,$Invocation.InvocationName.LastIndexOf("\"));
    }
}
Christian Flem
  • 680
  • 9
  • 7
  • 1
    why -Scope 1? not -Scope 0 – suiwenfeng Jan 19 '16 at 06:31
  • 1
    Get-Variable : The scope number '1' exceeds the number of active scopes. – suiwenfeng Jan 19 '16 at 06:32
  • 2
    You're getting this error because you have no parent scope. -Scope parameter gets the variable in a specified scope. 1 in this case is the parent scope. For more info see this technet article about Get-Variable ( https://technet.microsoft.com/en-us/library/hh849899.aspx ) – Christian Flem Jan 20 '16 at 09:16
22

Get-Location will return the current location:

$Currentlocation = Get-Location
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Veera Induvasi
  • 822
  • 7
  • 19
  • 5
    PS C:\Windows\system32> C:\powershell\checkfile.ps1 --> this will give c:\windows\system32 – nbi Apr 10 '17 at 18:15
12

I like the one-line solution :)

$scriptDir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
FrankyHollywood
  • 1,497
  • 19
  • 18
9

Try this:

$WorkingDir = Convert-Path .
Mohammad
  • 21,175
  • 15
  • 55
  • 84
5

In Powershell 3 and above you can simply use

$PSScriptRoot

BlackSpy
  • 5,563
  • 5
  • 29
  • 38
4

If you just need the name of the current directory, you could do something like this:

((Get-Location) | Get-Item).Name

Assuming you are working from C:\Temp\Location\MyWorkingDirectory>

Output

MyWorkingDirectory

SolThoth
  • 369
  • 3
  • 7
4

Most answers don't work when debugging in the following IDEs:

  • PS-ISE (PowerShell ISE)
  • VS Code (Visual Studio Code)

Because in those the $PSScriptRoot is empty and Resolve-Path .\ (and similars) will result in incorrect paths.

Freakydinde's answer is the only one that resolves those situations, so I up-voted that, but I don't think the Set-Location in that answer is really what is desired. So I fixed that and made the code a little clearer:

$directorypath = if ($PSScriptRoot) { $PSScriptRoot } `
    elseif ($psise) { split-path $psise.CurrentFile.FullPath } `
    elseif ($psEditor) { split-path $psEditor.GetEditorContext().CurrentFile.Path }
SteinarV
  • 57
  • 1
  • 8
Mariano Desanze
  • 7,847
  • 7
  • 46
  • 67
2

For what it's worth, to be a single-line solution, the below is a working solution for me.

$currFolderName = (Get-Location).Path.Substring((Get-Location).Path.LastIndexOf("\")+1)

The 1 at the end is to ignore the /.

Thanks to the posts above using the Get-Location cmdlet.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ak777
  • 346
  • 7
  • 18
2

this function will set the prompt location to script path, dealing with the differents way to get scriptpath between vscode, psise and pwd :

function Set-CurrentLocation
{
    $currentPath = $PSScriptRoot                                                                                                     # AzureDevOps, Powershell
    if (!$currentPath) { $currentPath = Split-Path $pseditor.GetEditorContext().CurrentFile.Path -ErrorAction SilentlyContinue }     # VSCode
    if (!$currentPath) { $currentPath = Split-Path $psISE.CurrentFile.FullPath -ErrorAction SilentlyContinue }                       # PsISE

    if ($currentPath) { Set-Location $currentPath }
}
freakydinde
  • 1,070
  • 1
  • 9
  • 10
1

You would think that using '.\' as the path means that it's the invocation path. But not all the time. Example, if you use it inside a job ScriptBlock. In which case, it might point to %profile%\Documents.

syanzy
  • 109
  • 1
  • 2
1

This is what I came up with. It's an array including multiple methods of finding a path, uses the current location, filters out null\empty results, and returns the first not-null value.

@((
  ($MyInvocation.MyCommand.Module.ModuleBase),
  ($PSScriptRoot),
  (Split-Path -Parent -Path $MyInvocation.MyCommand.Definition -ErrorAction SilentlyContinue),
  (Get-Location | Select-Object -ExpandProperty Path)
) | Where-Object { $_ })[0]
zman
  • 11
  • 1
1

To only get the current folder name, you can also use:

(Split-Path -Path (Get-Location) -Leaf)
ouflak
  • 2,458
  • 10
  • 44
  • 49
qcjuzzqq
  • 27
  • 6
-1

To expand on @Cradle 's answer: you could also write a multi-purpose function that will get you the same result per the OP's question:

Function Get-AbsolutePath {

    [CmdletBinding()]
    Param(
        [parameter(
            Mandatory=$false,
            ValueFromPipeline=$true
        )]
        [String]$relativePath=".\"
    )

    if (Test-Path -Path $relativePath) {
        return (Get-Item -Path $relativePath).FullName -replace "\\$", ""
    } else {
        Write-Error -Message "'$relativePath' is not a valid path" -ErrorId 1 -ErrorAction Stop
    }

}
Erutan409
  • 730
  • 10
  • 21
-1

I had similar problems and it made me a lot of trouble since I am making programs written in PowerShell (full end user GUI applications) and I have a lot of files and resources I need to load from disk. From my experience, using . to represent current directory is unreliable. It should represent current working directory, but it often does not. It appears that PowerShell saves location from which PowerShell has been invoked inside .. To be more precise, when PowerShell is first started, it starts, by default, inside your home user directory. That is usually directory of your user account, something like C:\USERS\YOUR USER NAME. After that, PowerShell changes directory to either directory from which you invoked it, or to directory where script you are executing is located before either presenting you with PowerShell prompt or running the script. But that happens after PowerShell app itself originally starts inside your home user directory.

And . represents that initial directory inside which PowerShell started. So . only represents current directory in case if you invoked PowerShell from the wanted directory. If you later change directory in PowerShell code, change appears not to be reflected inside . in every case. In some cases . represents current working directory, and in others directory from which PowerShell (itself, not the script) has been invoked, what can lead to inconsistent results. For this reason I use invoker script. PowerShell script with single command inside: POWERSHELL. That will ensure that PowerShell is invoked from the wanted directory and thus make . represent current directory. But it only works if you do not change directory later in PowerShell code. In case of a script, I use invoker script which is similar to last one I mentioned, except it contains a file option: POWERSHELL -FILE DRIVE:\PATH\SCRIPT NAME.PS1. That ensures that PowerShell is started inside current working directory.

Simply clicking on script invokes PowerShell from your home user directory no matter where script is located. It results with current working directory being directory where script is located, but PowerShell invocation directory being C:\USERS\YOUR USER NAME, and with . returning one of these two directories depending on the situation, what is ridiculous.

But to avoid all this fuss and using invoker script, you can simply use either $PWD or $PSSCRIPTROOT instead of . to represent current directory depending on weather you wish to represent current working directory or directory from which script has been invoked. And if you, for some reason, want to retrieve other of two directories which . returns, you can use $HOME.

I personally just have invoker script inside root directory of my apps I develop with PowerShell which invokes my main app script, and simply remember to never ever change current working directory inside my source code of my app, so I never have to worry about this, and I can use . to represent current directory and to support relative file addressing in my applications without any problems. This should work in newer versions of PowerShell (newer than version 2).

SYOB SYOT
  • 900
  • 10
  • 14
-1

Mine was a short, so unplug everything but USB from it and recompile

kris2340k
  • 29
  • 3