如何找到执行脚本的源路径?

时间:2009-04-29 11:42:07

标签: powershell powershell-v1.0

我希望能够告诉我执行脚本的运行路径。
这往往不是$ pwd。

我需要调用相对于我的脚本的文件夹结构中的其他脚本,虽然我可以对路径进行硬编码,但是当尝试从“dev”推广到“dev”时,这既令人反感又有点痛苦测试“到”生产“。

7 个答案:

答案 0 :(得分:95)

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

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

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

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

  1. 内联代码[inline]
  2. 内联函数,即主程序中的函数[内联函数]
  3. 点源函数,即相同的函数移动到单独的.ps1文件[点源]
  4. 模块功能,即同一功能移至单独的.psm1文件[module]
  5. 最后两列显示使用脚本范围(即$ script :)或父范围(使用-scope 1)的结果。 “脚本”的结果意味着调用正确地报告了脚本的位置。 “模块”结果表示调用报告了包含函数的模块的位置,而不是调用函数的脚本;这表明这两个函数的缺点是您无法将该函数放入模块中。

    除了表格中的显着观察之外,设置模块问题是使用父范围方法在大多数情况下失败(事实上,成功的频率是其成功的两倍)。

    table of input combinations

    最后,这是测试工具:

    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中引入的内容,但很可能在Jeffhell Snover发布他的示例时,PowerShell 1中不存在脚本范围。

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

    2011.09.12更新

    您可以在我刚刚发布的Simple-Talk.com上的文章中阅读有关模块的其他提示和技巧: Further Down the Rabbit Hole: PowerShell Modules and Encapsulation

答案 1 :(得分:19)

您为Powershell 1.0版标记了您的问题,但是,如果您有权访问Powershell 3.0版,那么您知道$PSCommandPath$PSScriptRoot可以让脚本路径更容易一些。请参阅"OTHER SCRIPT FEATURES" section on this page for more information.

答案 2 :(得分:11)

我们在大多数脚本中使用这样的代码已经好几年没有问题了:

#--------------------------------------------------------------------
# Dot source support scripts
#--------------------------------------------------------------------
$ScriptPath = $MyInvocation.MyCommand.Path
$ScriptDir  = Split-Path -Parent $ScriptPath
. $ScriptDir\BuildVars.ps1
. $ScriptDir\LibraryBuildUtils.ps1
. $ScriptDir\BuildReportUtils.ps1

答案 3 :(得分: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

答案 4 :(得分:5)

我认为您可以使用

找到正在运行的脚本的路径
$MyInvocation.MyCommand.Path

希望它有所帮助!

和Cédric

答案 5 :(得分:1)

这是PS中的一些奇怪的事情(至少在我看来)。我确信它有一个很好的理由,但对我来说仍然很奇怪。所以:

如果您在脚本中但不在函数中,那么$ myInvocation.InvocationName将为您提供包括脚本名称的完整路径。如果您在脚本中并且在函数内部,那么$ myInvocation.ScriptName将为您提供相同的功能。

答案 6 :(得分:0)

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

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

然后在MyModule.psd1中引用MyScriptFile.ps1。引用NestedModules数组中的.ps1将使函数处于模块会话状态而不是全局会话状态。 (How to Write a Module Manifest

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