确定当前PowerShell脚本位置的最佳方法是什么?


530

每当需要引用公共模块或脚本时,我都喜欢使用相对于当前脚本文件的路径。这样,我的脚本始终可以在库中找到其他脚本。

那么,确定当前脚本目录的最佳标准方法是什么?目前,我正在做:

$MyDir = [System.IO.Path]::GetDirectoryName($myInvocation.MyCommand.Definition)

我知道可以在模块(.psm1)中使用$PSScriptRoot此信息,但是在常规脚本(即.ps1文件)中没有设置此信息。

获取当前PowerShell脚本文件位置的规范方法是什么?


Answers:


865

PowerShell 3+

# This is an automatic variable set to the current file's/module's directory
$PSScriptRoot

PowerShell 2

在PowerShell 3之前,没有比在MyInvocation.MyCommand.Definition属性中查询常规脚本更好的方法了 。在我拥有的每个PowerShell脚本的顶部,我都有以下一行:

$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition

1
Split-Path在这里做什么用?
CMCDragonkai

7
Split-Path-Parent参数一起使用,以返回当前目录,而不包含当前正在执行的脚本的名称。
hjoelr

5
注意:在Linux / macOS上使用PowerShell,您的脚本必须具有.ps1扩展名才能填充PSScriptRoot / MyInvocation等。在此处查看错误报告:github.com/PowerShell/PowerShell/issues/4217
Dave Wood

3
除了潜在的有趣之处之外,还可以进行Quibble:is的v2-近似值$PSScriptRootSplit-Path -Parent应用于)$MyInvocation.MyCommand.Path,不是$MyInvocation.MyCommand.Definition,尽管在脚本的顶级范围内它们的行为相同(这是为此目的调用的唯一明智的方法)。在函数脚本块内调用时,前者返回空字符串,而后者以字符串的形式返回函数主体的/脚本块的定义(PowerShell源代码)。
mklement0

62

如果要创建V2模块,则可以使用名为的自动变量 $PSScriptRoot

从PS>帮助automatic_variable

$ PSScriptRoot
       包含执行脚本模块的目录。
       此变量允许脚本使用模块路径访问其他
       资源。

16
这是PS 3.0中所需的功能:$PSCommandPath Contains the full path and file name of the script that is being run. This variable is valid in all scripts.
CodeMonkeyKing 2013年

2
刚刚测试了$ PSScriptRoot并按预期工作。但是,如果在命令行中运行它,它将为您提供一个空字符串。如果在脚本中使用并且执行了脚本,它只会给您结果。这就是它的意思..
Farrukh Waheed 2013年

4
我糊涂了。这个答案说要使用PSScriptRoot for V2。另一个答案是PSScriptRoot适用于V3 +,适用于v2。

5
v2中的@user $ PSScriptRoot仅适用于模块,如果您不在模块中编写“普通”脚本,则需要$ MyInvocation.MyCommand.Definition,请参见最佳答案。
yzorg 2015年

1
@Lofful我在“ v2”中说过,它仅是为模块定义的。您是说它是在v3中的模块外部定义的。我想我们在说同样的话。:)
yzorg

35

对于PowerShell 3.0

$PSCommandPath
    Contains the full path and file name of the script that is being run. 
    This variable is valid in all scripts.

该函数是:

function Get-ScriptDirectory {
    Split-Path -Parent $PSCommandPath
}

20
更好的是,使用$ PSScriptRoot。它是当前文件/模块的目录。
亚伦詹森

2
该命令包括脚本的文件名,这使我不知所措。当您需要路径时,您可能也不想在其中使用脚本名称。至少,我想不出您想要的理由。$ PSScriptRoot不包含文件名(从其他答案中收集)。
YetAnotherRandomUser

$ PSScriptRoot在常规PS1脚本中为空。不过,$ PSCommandPath可以工作。根据其他帖子中给出的描述,这两种行为都是可以预期的。也可以只使用[IO.Path] :: GetDirectoryName($ PSCommandPath)来获取没有文件名的脚本目录。
失败

19

对于PowerShell 3+

function Get-ScriptDirectory {
    if ($psise) {
        Split-Path $psise.CurrentFile.FullPath
    }
    else {
        $global:PSScriptRoot
    }
}

我已经将此功能放置在我的个人资料中。它也可以使用F8/ Run Selection 在ISE中运行。


16

也许我在这里遗漏了一些东西……但是,如果您想要当前的工作目录,则可以使用它:(Get-Location).Path用于字符串或Get-Location对象。

除非您指的是这样的东西,否则在再次阅读问题后我会理解的。

function Get-Script-Directory
{
    $scriptInvocation = (Get-Variable MyInvocation -Scope 1).Value
    return Split-Path $scriptInvocation.MyCommand.Path
}

16
这将获取用户正在运行脚本的当前位置。不是脚本文件本身的位置。
亚伦詹森

2
function Get-Script-Directory { $scriptInvocation = (Get-Variable MyInvocation -Scope 1).Value return Split-Path $scriptInvocation.MyCommand.Path } $hello = "hello" Write-Host (Get-Script-Directory) Write-Host $hello 保存该文件,然后从其他目录运行它。您将显示脚本的路径。
肖恩·C,

那是一个很好的功能,我需要什么,但是我如何在所有脚本中共享和使用它呢?这是一个鸡与蛋的问题:我想使用一个函数来查找我的当前位置,但是我需要我的位置才能加载该函数。
亚伦詹森

2
注意:此函数的调用必须在脚本的顶层,如果它嵌套在另一个函数中,则必须更改“ -Scope”参数以指定您在调用堆栈中的深度。
肯尼2015年

11

与已经发布的答案非常相似,但是管道似乎更类似于PowerShell:

$PSCommandPath | Split-Path -Parent

11

我使用自动变量 $ExecutionContext。它将在PowerShell 2及更高版本中运行。

 $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath('.\')

$ ExecutionContext包含一个EngineIntrinsics对象,该对象表示Windows PowerShell主机的执行上下文。您可以使用此变量来查找可用于cmdlet的执行对象。


1
这是它唯一为我工作的程序,试图从STDIN中获取powershell。
塞巴斯蒂安

当您在UNC路径上下文中时,此解决方案也可以正常工作。
user2030503

1
显然,这是在拾取工作目录-而不是脚本所在的位置?
monojohnny

@monojohnny是的,这基本上是当前的工作目录,并且从另一个位置调用脚本时将不起作用。
marsze

9

我花了一段时间才开发出一些可以接受的答案,并将其转变为强大的功能。

我不确定其他人,但是我在PowerShell版本2和3上都装有机器的环境中工作,因此我需要同时处理这两个。以下功能提供了优美的后备:

Function Get-PSScriptRoot
{
    $ScriptRoot = ""

    Try
    {
        $ScriptRoot = Get-Variable -Name PSScriptRoot -ValueOnly -ErrorAction Stop
    }
    Catch
    {
        $ScriptRoot = Split-Path $script:MyInvocation.MyCommand.Path
    }

    Write-Output $ScriptRoot
}

这也意味着该函数引用的是Script范围,而不是Michael Sorens 在其博客文章之一中概述的父级范围。


谢谢!我需要“ $ script:”来使其在Windows PowerShell ISE中工作。
Kirk Liemohn '17

谢谢你 这是唯一对我有用的。通常,在获得Get-location之类的功能之前,我必须将CD放入脚本所在的目录中。我想知道为什么PowerShell无法自动更新目录。
Zain

7

我需要知道脚本名称及其在哪里执行。

在主脚本和导入的.PSM1库文件的主行中调用时,在MyInvocation结构前面加上“ $ global:”前缀将返回完整路径和脚本名称。它也可以在导入的库中的函数中工作。

经过反复摆弄之后,我决定使用$ global:MyInvocation.InvocationName。它可与CMD启动,Run With Powershell和ISE可靠地配合使用。本地和UNC启动都返回正确的路径。


4
拆分路径-路径$($ global:MyInvocation.MyCommand.Path)非常感谢。其他解决方案返回了调用应用程序的路径。
2014年

1
注意事项:在ISE中,使用F8 /运行选择调用此功能将触发ParameterArgumentValidationErrorNullNotAllowed异常。
堰2015年

5

我总是使用这个小片段,以相同的方式适用于PowerShell和ISE

# Set active path to script-location:
$path = $MyInvocation.MyCommand.Path
if (!$path) {
    $path = $psISE.CurrentFile.Fullpath
}
if ($path) {
    $path = Split-Path $path -Parent
}
Set-Location $path

3

我发现此处发布的较旧的解决方案不适用于PowerShell V5。我想出了这个:

try {
    $scriptPath = $PSScriptRoot
    if (!$scriptPath)
    {
        if ($psISE)
        {
            $scriptPath = Split-Path -Parent -Path $psISE.CurrentFile.FullPath
        }
        else {
            Write-Host -ForegroundColor Red "Cannot resolve script file's path"
            exit 1
        }
    }
}
catch {
    Write-Host -ForegroundColor Red "Caught Exception: $($Error[0].Exception.Message)"
    exit 2
}

Write-Host "Path: $scriptPath"

2

您可能还会考虑 split-path -parent $psISE.CurrentFile.Fullpath其他任何方法是否失败。特别是,如果您运行文件以加载一堆函数,然后在ISE Shell中使用-来执行这些函数(或者如果您运行选择),则上述Get-Script-Directory函数似乎无法正常工作。


3
$PSCommandPath只要您先保存脚本并执行整个文件,它将在ISE中工作。否则,您实际上并没有执行脚本。您只是将命令“粘贴”到外壳中。
Zenexer

@Zenexer我认为那是我当时的目标。虽然如果我的目标与最初的目标不符,那么这对偶尔的Google员工来说可能并没有太大帮助...

2

利用所有这些答案和评论中的内容,我将其汇总给以后看到此问题的任何人。它涵盖了其他答案中列出的所有情况

    # If using ISE
    if ($psISE) {
        $ScriptPath = Split-Path -Parent $psISE.CurrentFile.FullPath
    # If Using PowerShell 3 or greater
    } elseif($PSVersionTable.PSVersion.Major -gt 3) {
        $ScriptPath = $PSScriptRoot
    # If using PowerShell 2 or lower
    } else {
        $ScriptPath = split-path -parent $MyInvocation.MyCommand.Path
    }

-4
function func1() 
{
   $inv = (Get-Variable MyInvocation -Scope 1).Value
   #$inv.MyCommand | Format-List *   
   $Path1 = Split-Path $inv.scriptname
   Write-Host $Path1
}

function Main()
{
    func1
}

Main
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.