【问题标题】:How to get the current directory of the cmdlet being executed如何获取正在执行的 cmdlet 的当前目录
【发布时间】:2012-01-10 17:47:00
【问题描述】:

这应该是一个简单的任务,但我已经看到了几次尝试如何获取执行的 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的配置方式有关系吗?

【问题讨论】:

标签: powershell cmdlet


【解决方案1】:

执行此操作的可靠方法就像您展示的 $MyInvocation.MyCommand.Path

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

PowerShell v3+

使用自动变量$PSScriptRoot

【讨论】:

  • 您能解释一下您是如何找到属性 PATH 的吗? $MyInvocation.MyCommand|gm 不在成员列表中显示此类属性。
  • 为什么不直接使用 $PSScriptRoot?似乎更可靠
  • @user2326106 你能解释一下$PSScriptRoot$MyInvocation.MyCommand.Path之间的区别吗?
  • @VitaliyMarkitanov 您是否在脚本中运行了$MyInvocation.MyCommand | gmPath指的是脚本文件的全路径,所以直接在终端执行是找不到的。
  • 我在使用$MyInvocation.MyCommand.Path\File.csv 时遇到了问题,在我用谷歌搜索了$MyInvocation.MyCommand.Path 之后,我发现了这个MS article 的详细信息,我必须将其中的一部分拆分并放入变量中,然后才能让它工作: $myDir = Split-Path -Parent $MyInvocation.MyCommand.Path 。对powershell非常陌生,所以问题可能是我缺乏理解
【解决方案2】:

路径通常为空。这个功能更安全。

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

【讨论】:

  • 为什么是 -Scope 1?不是 -Scope 0
  • Get-Variable : 范围号“1”超过了活动范围的数量。
  • 您收到此错误是因为您没有父范围。 -Scope 参数获取指定范围内的变量。在这种情况下,1 是父范围。有关详细信息,请参阅这篇关于 Get-Variable (technet.microsoft.com/en-us/library/hh849899.aspx) 的 technet 文章
【解决方案3】:

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

【讨论】:

    【解决方案4】:

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

    (Get-Item .).FullName
    

    【讨论】:

    • 谢谢,这是从相对路径中找到完整路径的好方法。例如。 (Get-Item -Path $myRelativePath -Verbose).FullName
    • 谢谢你。其他答案不适用于编译为 EXE 的 Powershell 脚本。
    • 这是错误。这将获取 process 的当前目录,该目录可以在任何位置。例如,如果我的命令行当前目录是C:\mydir,并且我调用命令C:\dir1\dir2\dir3\mycmdlet.ps1,那么这将解析为C:\mydir,而不是C:\dir1\dir2\dir3。调用一个新的可执行文件也有同样的问题,因为当前目录是从父进程继承的。
    • 工作就像一个魅力
    【解决方案5】:

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

     $PSScriptRoot
    

    about_Automatic_Variablesabout_Scripts 两个状态:

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

    我是这样使用的:

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

    【讨论】:

    • 它是特定于版本的。这至少需要 Powershell 3.0。
    • 这是我需要引用与脚本位于同一位置的文件 - 谢谢!
    • 这是最好的答案,因为它准确地为您提供了 PS 脚本所在的路径,它本质上是脚本执行的根目录。它不关心您调用脚本的当前工作目录是什么。 +1。
    • @MarvinDickhaus 这就是为什么在大多数脚本中需要使用“Set-StrictMode -Version 3.0”的原因:)非常感谢您提供的链接!
    【解决方案6】:

    你也可以使用:

    (Resolve-Path .\).Path
    

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

    (自 PowerShell 2.0 起可用。)

    【讨论】:

    • 这是错误。这将获取 process 的当前目录,该目录可以在任何地方。例如,如果我的命令行当前目录是C:\mydir,并且我调用了命令C:\dir1\dir2\dir3\mycmdlet.ps1,那么这将解析为C:\mydir,而不是C:\dir1\dir2\dir3。调用新的可执行文件也有同样的问题,因为当前目录是从父进程继承的。
    • 谢谢!我也误解了这个问题的标题,这个答案正是我想要的。但是......它没有回答这个问题。
    【解决方案7】:

    我喜欢the one-line solution :)

    $scriptDir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
    

    【讨论】:

    • 对我有用。我的密码是我的用户目录,我在另一个目录 (B) 中运行脚本 (B)
    【解决方案8】:

    试试:

    (Get-Location).path
    

    或:

    ($pwd).path
    

    【讨论】:

    • 每当我从 powershell 中休息很长时间时,我都会忘记$pwd/$PWD!更有用的 IMO...
    【解决方案9】:

    要扩展@Cradle 的答案:您还可以编写multi-purpose function,根据 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
        }
    
    }
    

    【讨论】:

      【解决方案10】:

      Get-Location 将返回当前位置:

      $Currentlocation = Get-Location
      

      【讨论】:

      • PS C:\Windows\system32> C:\powershell\checkfile.ps1 --> 这将给出 c:\windows\system32
      【解决方案11】:

      试试这个:

      $WorkingDir = Convert-Path .
      

      【讨论】:

        【解决方案12】:

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

        $PSScriptRoot

        【讨论】:

          【解决方案13】:

          对于它的价值,作为单行解决方案,以下对我来说是一个可行的解决方案。

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

          最后的1是忽略/

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

          【讨论】:

            【解决方案14】:

            如果你只需要当前目录的名字,你可以这样做:

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

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

            输出

            我的工作目录

            【讨论】:

              【解决方案15】:

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

              . 表示 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 版更新)。

              【讨论】:

                【解决方案16】:

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

                【讨论】:

                  【解决方案17】:

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

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

                  因为在那些$PSScriptRoot 是空的,Resolve-Path .\(和类似的)会导致不正确的路径。

                  Freakydinde's answer 是唯一解决这些情况的方法,所以我对此表示赞同,但我认为该答案中的Set-Location 并不是真正需要的。所以我修复了这个问题并使代码更清晰:

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

                  【讨论】:

                  • 这是我发现在 PowerShell ISE 和 VS Code 中都有效的唯一选项。
                  【解决方案18】:

                  这就是我想出的。它是一个数组,包含多种查找路径的方法,使用当前位置,过滤掉 null\empty 结果,并返回第一个非 null 值。

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

                  【讨论】:

                    【解决方案19】:

                    要只获取当前文件夹名称,还可以使用:

                    (Split-Path -Path (Get-Location) -Leaf)
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2015-09-02
                      • 2023-03-29
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2011-01-18
                      • 2011-02-19
                      • 2022-06-14
                      相关资源
                      最近更新 更多