64

我希望能够知道我的执行脚本是从哪个路径运行的。
这通常不是 $pwd。

我需要调用与我的脚本相关的文件夹结构中的其他脚本,虽然我可以对路径进行硬编码,但在尝试从“开发”升级到“测试”时,这既令人反感,又有点让人头疼“生产”。

4

7 回答 7

101

最初由PowerShell 团队的Jeffrey Snover发布的无处不在的脚本(在Skyler 的回答中给出)以及 Keith Cedirc 和 EBGreen 发布的变体都存在一个严重的缺陷——代码是否报告您所期望的取决于您调用它的位置!

我下面的代码通过简单地引用脚本范围而不是范围来克服这个问题:

function Get-ScriptDirectory
{
    Split-Path $script:MyInvocation.MyCommand.Path
}

为了说明这个问题,我创建了一个测试工具,它以四种不同的方式评估目标表达式。(括号中的术语是以下结果表的关键。)

  1. 内联代码 [inline]
  2. 内联函数,即主程序中的函数【内联函数】
  3. 点源函数,即相同的函数移动到单独的 .ps1 文件 [点源]
  4. 模块函数,即相同的函数移动到单独的 .psm1 文件 [模块]

最后两列显示了使用脚本作用域(即 $script:)或使用父作用域(使用 -scope 1)的结果。“脚本”的结果意味着调用正确地报告了脚本的位置。“模块”结果意味着调用报告了包含该函数的模块的位置,而不是调用该函数的脚本;这表明这两个函数都有一个缺点,即您不能将函数放入模块中。

撇开模块问题不谈,表中的显着观察是,使用父作用域方法在大多数情况下都失败了(实际上,成功率是成功率的两倍)。

输入组合表

最后,这是测试车辆:

function DoubleNested()
{
    "=== DOUBLE NESTED ==="
    NestCall
}

function NestCall()
{
    "=== NESTED ==="
    "top level:"
    Split-Path $script:MyInvocation.MyCommand.Path
    #$foo = (Get-Variable MyInvocation -Scope 1).Value
    #Split-Path $foo.MyCommand.Path
    "immediate func call"
    Get-ScriptDirectory1
    "dot-source call"
    Get-ScriptDirectory2
    "module call"
    Get-ScriptDirectory3
}

function Get-ScriptDirectory1
{
    Split-Path $script:MyInvocation.MyCommand.Path
    # $Invocation = (Get-Variable MyInvocation -Scope 1).Value
    # Split-Path $Invocation.MyCommand.Path
}

. .\ScriptDirFinder.ps1
Import-Module ScriptDirFinder -force

"top level:"
Split-Path $script:MyInvocation.MyCommand.Path
#$foo = (Get-Variable MyInvocation -Scope 1).Value
#Split-Path $foo.MyCommand.Path

"immediate func call"
Get-ScriptDirectory1
"dot-source call"
Get-ScriptDirectory2
"module call"
Get-ScriptDirectory3

NestCall
DoubleNested

ScriptDirFinder.ps1 的内容:

function Get-ScriptDirectory2
{
    Split-Path $script:MyInvocation.MyCommand.Path
#   $Invocation = (Get-Variable MyInvocation -Scope 1).Value
#   Split-Path $Invocation.MyCommand.Path
}

ScriptDirFinder.psm1 的内容:

function Get-ScriptDirectory3
{
    Split-Path $script:MyInvocation.MyCommand.Path
#   $Invocation = (Get-Variable MyInvocation -Scope 1).Value
#   Split-Path $Invocation.MyCommand.Path
}

我不熟悉 PowerShell 2 中引入的内容,但很可能是 PowerShell 1 中不存在脚本范围,当时 Jeffrey Snover 发布了他的示例。

当我发现他的代码示例在网络上广泛传播时,我感到很惊讶,但当我尝试时它立即失败了!但那是因为我使用它与 Snover 的示例不同(我不是在脚本顶部而是从另一个函数内部调用它(我的“嵌套两次”示例)。)

2011.09.12 更新

您可以在我刚刚在 Simple-Talk.com 上发表的文章中阅读有关模块的其他提示和技巧: 进一步深入兔子洞:PowerShell 模块和封装

于 2011-08-08T16:35:39.357 回答
22

您为 Powershell 1.0 版标记了您的问题,但是,如果您可以访问您知道的 Powershell 3.0 版$PSCommandPath$PSScriptRoot这使得获取脚本路径更容易一些。有关详细信息,请参阅此页面上的“其他脚本功能”部分。

于 2013-10-09T23:44:38.280 回答
11

几年来,我们在大多数脚本中一直使用这样的代码,没有任何问题:

#--------------------------------------------------------------------
# Dot source support scripts
#--------------------------------------------------------------------
$ScriptPath = $MyInvocation.MyCommand.Path
$ScriptDir  = Split-Path -Parent $ScriptPath
. $ScriptDir\BuildVars.ps1
. $ScriptDir\LibraryBuildUtils.ps1
. $ScriptDir\BuildReportUtils.ps1
于 2009-05-01T18:21:01.010 回答
6

我最近遇到了同样的问题。以下文章帮助我解决了这个问题:http: //blogs.msdn.com/powershell/archive/2007/06/19/get-scriptdirectory.aspx

如果您对它的工作原理不感兴趣,这里是您在文章中需要的所有代码:

function Get-ScriptDirectory
{
$Invocation = (Get-Variable MyInvocation -Scope 1).Value
Split-Path $Invocation.MyCommand.Path
}

然后您只需执行以下操作即可获得路径:

$path = Get-ScriptDirectory
于 2009-04-29T16:58:57.700 回答
5

我认为您可以使用找到运行脚本的路径

$MyInvocation.MyCommand.Path

希望能帮助到你 !

塞德里克

于 2009-04-29T13:41:41.900 回答
1

这是 PS 中的怪事之一(至少在我看来)。我敢肯定这是有充分理由的,但对我来说仍然很奇怪。所以:

如果您在脚本中但不在函数中,则 $myInvocation.InvocationName 将为您提供包括脚本名称在内的完整路径。如果你在一个脚本和一个函数中,那么 $myInvocation.ScriptName 会给你同样的东西。

于 2009-04-29T13:34:21.450 回答
0

谢谢msorens!这真的帮助了我的自定义模块。如果有人有兴趣自己制作,这就是我的结构。

MyModule (folder)
 - MyModule.psd1 (help New-ModuleManifest)
 - MyScriptFile.ps1 (ps1 files are easy to test)

然后在 MyModule.psd1 中引用 MyScriptFile.ps1。引用 NestedModules 数组中的 .ps1 会将函数置于模块会话状态而不是全局会话状态。(如何编写模块清单

NestedModules = @('.\MyScriptFile.ps1','.\MyOtherScriptFile.ps1')

MyScriptFile.ps1 的内容

function Get-ScriptDirectory {
    Split-Path $script:MyInvocation.MyCommand.Path
}

try {
    Export-ModuleMember -Function "*-*"
}
catch{}

运行 MyScriptFile.ps1 时,try/catch 隐藏了 Export-ModuleMember 的错误

MyModule目录复制到此处找到的路径之一$env:PSModulePath

PS C:\>Import-Module MyModule
PS C:\>Get-Command -Module MyModule

CommandType     Name                                               ModuleName                                                                                                                                                
-----------     ----                                               ----------                                                                                                                                                
Function        Get-ScriptDirectory                                MyModule  
于 2013-01-30T21:27:37.297 回答