如何获取正在执行的cmdlet的当前目录

268

这应该是一个简单的任务,但我看到了多个获取执行命令的目录路径的尝试,有些成功,有些失败。例如,当我执行 C:\temp\myscripts\mycmdlet.ps1 时,它在 C:\temp\myscripts\settings.xml 中有一个设置文件,我希望能够在 mycmdlet.ps1 中将C:\temp\myscripts 存储到一个变量中。

以下是一种可行的解决方案(虽然有点复杂):

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

另一个人建议了这个解决方案,但只适用于我们的测试环境:

$settingspath = '.\settings.xml'

我非常喜欢后一种方法,比起每次解析文件路径作为参数,我更喜欢这种方法,但是在我的开发环境中,我无法使其工作。我应该怎么做?这与PowerShell的配置有关吗?


13
请注意,这个问题标题的含糊不清导致以下回答解决了两个不同的问题之一,但没有明确说明是哪一个问题:(a) 如何引用“当前位置” (目录),或者 (b) 如何引用“正在运行的脚本”的位置 (正在运行的脚本所在的目录,可能与当前目录相同,也可能不同)。 - mklement0
20个回答

311

是的,那应该可以。但如果你需要看到绝对路径,这就是你所需要的:

(Get-Item .).FullName

5
谢谢,这是一种很好的方法来寻找相对路径的完整路径。例如(Get-Item -Path $myRelativePath -Verbose).FullName。 - dlux
谢谢您。其他答案对于编译为EXE的Powershell脚本无法正常工作。 - Zach Alexander
23
这是错误的。这将获取进程的当前目录,它可以在任何地方。例如,如果我的命令行当前目录是C:\mydir,并且我调用命令C:\dir1\dir2\dir3\mycmdlet.ps1,那么它将解析为C:\mydir,而不是C:\dir1\dir2\dir3。调用新的可执行文件也有相同的问题,因为当前目录是从父进程继承的。 - jpmc26
运行得非常顺利 - Aquaphor

197

可靠的方法就像你展示的$MyInvocation.MyCommand.Path一样。

在PowerShell中,使用相对路径将基于$pwd,即应用程序的当前目录或.NET API的当前工作目录。

PowerShell v3+:

使用自动变量$PSScriptRoot


6
你能否请解释一下你是如何找到属性PATH的吗?$MyInvocation.MyCommand|gm在成员列表中没有显示出这样的属性。 - Vitaliy Markitanov
24
为什么不直接使用$PSScriptRoot?它似乎更可靠。 - mBrice1024
@user2326106,你能解释一下$PSScriptRoot$MyInvocation.MyCommand.Path之间的区别吗? - duct_tape_coder
1
@VitaliyMarkitanov 你在脚本中运行了 $MyInvocation.MyCommand | gm 吗? Path 指的是脚本文件的完整路径,因此您无法直接在终端中执行它。 - Connor Low
我在处理$MyInvocation.MyCommand.Path\File.csv时遇到了问题,经过搜索$MyInvocation.MyCommand.Path后,我发现这篇微软文章详细说明了我必须将其中的一部分拆分并放入变量中,然后才能使其正常工作:$myDir = Split-Path -Parent $MyInvocation.MyCommand.Path。由于我对PowerShell非常陌生,所以问题很可能是我理解不足。 - gregg

98
使用以下预定义变量似乎是最简单的方法:
 $PSScriptRoot

about_Automatic_Variablesabout_Scripts都说明:

在 PowerShell 2.0 中,此变量仅在脚本模块(.psm1)中有效。从 PowerShell 3.0 开始,在所有脚本中都有效。

我是这样使用它的:

 $MyFileName = "data.txt"
 $filebase = Join-Path $PSScriptRoot $MyFileName

13
版本要求具体,至少需要 Powershell 3.0。 - Marvin Dickhaus
1
这正是我需要的,以便在与脚本相同的位置引用文件 - 谢谢! - Adam Prescott
这是最好的答案,因为它精确地给出了您的PS脚本所在的路径,这实际上是您的脚本执行的根目录。它不关心您从哪个当前工作目录调用脚本。+1。 - RBT
这就是为什么在大多数脚本中需要使用“Set-StrictMode -Version 3.0” :)感谢提供链接! - Alexander Shapkin

48

尝试:

(Get-Location).path
或:
($pwd).path

2
每次我长时间离开 PowerShell 后,我总是会忘记 $pwd/$PWD!在我看来,这个命令非常有用... - kayleeFrye_onDeck

47

您还可以使用:

(Resolve-Path .\).Path

方括号中的内容返回一个PathInfo对象。

(自PowerShell 2.0起可用。)


4
这是错误的。 这会获取进程的当前目录,而该进程可以位于任何位置。 例如,如果我的命令行当前目录为C:\mydir,并且我调用命令C:\dir1\dir2\dir3\mycmdlet.ps1,则这将解析为C:\mydir,而不是C:\dir1\dir2\dir3。 调用新可执行文件也存在相同的问题,因为当前目录是从父进程继承的。 - jpmc26
4
谢谢!我也误解了这个问题的标题,而这个答案正是我寻找的。然而...它并没有回答我的问题。 - Ryan Leach

33

路径通常为空。使用此函数更为安全。

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("\"));
    }
}

1
为什么要使用-Scope 1而不是-Scope 0? - suiwenfeng
1
Get-Variable:作用域编号“1”超出了活动作用域数量。 - suiwenfeng
2
你之所以会遇到这个错误,是因为你没有父级作用域。-Scope参数获取指定作用域中的变量。在这种情况下,1是父级作用域。有关Get-Variable的更多信息,请参阅此Technet文章(https://technet.microsoft.com/en-us/library/hh849899.aspx)。 - Christian Flem

22

Get-Location 命令将返回当前位置:

$Currentlocation = Get-Location

5
PS C:\Windows\system32> C:\powershell\checkfile.ps1 --> 这将显示 c:\windows\system32 文件的信息。 - nbi

12

可以,对我有效。我的 pwd 是我的用户目录,我正在另一个目录(B)中运行脚本,它会给出(B)。 - andrew pate

9

试试这个:

$WorkingDir = Convert-Path .

5
在Powershell 3及以上版本中,您可以简单地使用$PSScriptRoot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接