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


202

这应该是一个简单的任务,但是我已经看过几次尝试如何成功获取到执行的cmdlet所在目录的路径。例如,当我执行时C:\temp\myscripts\mycmdlet.ps1有一个设置文件时,C:\temp\myscripts\settings.xml我希望能够存储C:\temp\myscripts在中的变量中mycmdlet.ps1

这是一种可行的解决方案(尽管有点麻烦):

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

另一个人建议此解决方案仅在我们的测试环境中有效:

$settingspath = '.\settings.xml'

我非常喜欢后一种方法,而不是每次都必须将文件路径解析为参数,但我无法使其在我的开发环境中正常工作。我该怎么办?它与PowerShell的配置有关吗?


11
请注意,这个问题的模棱两可导致下面的答案解决了两个不同的问题之一,而没有明确说明:(a)如何引用当前位置(目录)(b)如何引用运行脚本的位置(正在运行的脚本所在的目录(可能不是当前目录)。
mklement0

Answers:


143

可靠的方法就像您演示的那样$MyInvocation.MyCommand.Path

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

PowerShell v3 +

使用自动变量$PSScriptRoot


6
能否请您解释一下如何找到属性PATH?$ MyInvocation.MyCommand | gm在成员列表中不显示此类属性。
Vitaliy Markitanov

19
为什么不只使用$ PSScriptRoot?似乎更可靠
mBrice1024 '17

@ user2326106你能解释一下之间的区别$PSScriptRoot$MyInvocation.MyCommand.Path
–duct_tape_coder,

1
这个答案是不完整的。
K-SO的毒性在增加。

这个答案是不完整的。
stackleit

263

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

(Get-Item -Path ".\").FullName

4
谢谢,这是从相对路径中查找完整路径的好方法。例如(Get-Item -Path $ myRelativePath -Verbose).FullName
dlux

这次真是万分感谢。对于编译为EXE的Powershell脚本,其他答案无效。
Zach Alexander

10
这是错的。这将获取该进程的当前目录,该目录可以在任何位置。例如,如果我的命令行当前目录是C:\mydir,并且我调用该命令C:\dir1\dir2\dir3\mycmdlet.ps1,那么它将解析为C:\mydir,而不是C:\dir1\dir2\dir3。调用新的可执行文件存在相同的问题,因为当前目录是从父进程继承的。
jpmc26 '18

85

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

 $PSScriptRoot

about_Automatic_Variablesabout_Scripts这两种状态:

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

我这样使用它:

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

12
它是特定于版本的。这至少需要Powershell 3.0。
Marvin Dickhaus

1
这就是我需要在与脚本相同的位置引用文件的原因,谢谢!
亚当·普雷斯科特

这是最好的答案,因为它可以精确地为您提供PS脚本所在的路径,该路径实际上是脚本执行的根。不管您从哪里调用脚本,当前的工作目录是什么。+1。
RBT

@MarvinDickhaus这就是为什么在大多数脚本中都需要使用“ Set-StrictMode -Version 3.0”的原因:)非常感谢您的链接!
亚历山大·沙普金

44

您还可以使用:

(Resolve-Path .\).Path

括号中的部分返回一个 PathInfo对象。

(自PowerShell 2.0起可用。)


2
这是错的。这将获取该进程的当前目录,该目录可以在任何位置。例如,如果我的命令行当前目录是C:\mydir,并且我调用该命令C:\dir1\dir2\dir3\mycmdlet.ps1,那么它将解析为C:\mydir,而不是C:\dir1\dir2\dir3。调用新的可执行文件存在相同的问题,因为当前目录是从父进程继承的。
jpmc26

3
谢谢!我也误解了这个问题的标题,而这个答案正是我所要的。但是...它没有回答问题。
Ryan The 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
为什么-范围1?不是-范围0
suiwenfeng

1
Get-Variable:作用域编号“ 1”超出了活动作用域的数量。
suiwenfeng '16

2
由于没有父级作用域,因此出现此错误。-Scope参数获取指定范围内的变量。在这种情况下,1是父范围。欲了解更多信息请参见关于获取变量此TechNet文章(technet.microsoft.com/en-us/library/hh849899.aspx
基督教Flem


17

Get-Location 将返回当前位置:

$Currentlocation = Get-Location

3
PS C:\ Windows \ system32> C:\ powershell \ checkfile.ps1->这将给c:\ windows \ system32
nbi



4

在Powershell 3及更高版本中,您可以简单地使用

$PSScriptRoot


1

您可能会认为使用“。\”作为路径意味着它是调用路径。但并非一直如此。例如,如果您在作业ScriptBlock中使用它。在这种情况下,它可能指向%profile%\ Documents。


1

此函数将提示位置设置为脚本路径,处理在vscode,psise和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 }
}

0

就单行解决方案而言,以下是对我有用的解决方案。

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

最后的1是忽略 /

感谢上面使用Get-Location cmdlet 的帖子。


0

在以下IDE中进行调试时,大多数答案不起作用:

  • PS-ISE(PowerShell ISE)
  • VS代码(Visual Studio代码)

因为在那些中,和$PSScriptRoot是空的Resolve-Path .\(和类似的东西)将导致错误的路径。

Freakydinde的答案是解决这些情况的唯一方法,因此我投票赞成,但我认为Set-Location答案中的答案并不是真正想要的。因此,我对其进行了修复,并使代码更加清晰:

$directorypath = if ($PSScriptRoot) { $PSScriptRoot } `
    elseif ($psise) { $psise.CurrentFile.FullPath } `
    elseif ($psEditor) { split-path $psEditor.GetEditorContext().CurrentFile.Path }

-1

要扩展@Cradle的答案:您还可以编写一个多用途函数,根据OP的问题,您将获得相同的结果:

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
    }

}

-1

如果只需要当前目录的名称,则可以执行以下操作:

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

假设您正在C:\ Temp \ Location \ MyWorkingDirectory>中工作

输出量

我的工作目录


-1

我遇到了类似的问题,这使我很麻烦,因为我正在制作用PowerShell(完整的最终用户GUI应用程序)编写的程序,并且我需要从磁盘加载很多文件和资源。根据我的经验,使用。之后,PowerShell会在向您显示PowerShell提示符或运行脚本之前,将目录更改为您从中调用该目录的目录或您正在执行的脚本所在的目录。但这是在PowerShell应用程序本身最初在您的主用户目录内启动之后发生的。.用于表示当前目录是不可靠的。它应该代表当前的工作目录,但通常不是。看来PowerShell会保存其中已从中调用PowerShell的位置.。更确切地说,首次启动PowerShell时,默认情况下,它将在您的主用户目录中启动。通常是您的用户帐户目录,例如C:\USERS\YOUR USER NAME

并且.代表PowerShell在其中启动的初始目录。因此,.仅当您从所需目录中调用PowerShell时,才表示当前目录。如果以后在PowerShell代码中更改目录,则更改似乎不会.在每种情况下都反映在其中。在某些情况下.代表当前的工作目录,而在另一些情况下代表从中调用PowerShell(本身,而不是脚本)的目录,则可能导致不一致的结果。因此,我使用调用程序脚本。内含单个命令的PowerShell脚本: POWERSHELL。这将确保从所需目录中调用PowerShell,从而使.代表当前目录。但是,只有在以后在PowerShell代码中不更改目录时,它才有效。对于脚本,我使用的调用程序脚本与我提到的上一个脚本相似,不同之处在于它包含一个文件选项: POWERSHELL -FILE DRIVE:\PATH\SCRIPT NAME.PS1。这样可以确保PowerShell在当前工作目录中启动。

无论脚本位于何处,只需单击脚本即可从您的主用户目录调用PowerShell。结果导致当前工作目录是脚本所在的目录,而PowerShell调用目录是C:\USERS\YOUR USER NAME,并且.根据情况返回这两个目录之一,这是荒谬的。

但是要避免所有这些麻烦并使用调用程序脚本,您可以根据天气情况简单地使用$PWD$PSSCRIPTROOT代替.表示当前目录,而希望表示当前的工作目录或从中调用脚本的目录。如果由于某种原因要检索.返回的两个目录中的另一个,则可以使用$HOME

我个人只是在使用PowerShell开发的应用程序的根目录中包含调用程序脚本,该脚本会调用我的主应用程序脚本,并且只记得永远不要在应用程序的源代码中更改当前的工作目录,所以我不必为此担心,而且我可以.用来表示当前目录并支持我的应用程序中的相对文件寻址,而不会出现任何问题。这应该在PowerShell的较新版本(比版本2更高)中起作用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.