如何获取当前 PowerShell 执行文件?

125

注意:PowerShell 1.0
我想获取当前正在执行的PowerShell文件名。也就是说,如果我像这样启动我的会话:

powershell.exe .\myfile.ps1

我想要得到字符串".\myfile.ps1"(或类似的内容)。编辑:"myfile.ps1" 更好。
有什么想法吗?


谢谢,目前的答案几乎都是相同的,但我只需要文件名(而不是整个路径),因此被接受的答案是@Keith的。两个答案都给了我帮助,现在我知道关于$MyInvocation的事情了 :-) - Ron Klein
如何从一个被包含的脚本中获取父级脚本? - Florin Sabau
12个回答

120

我尝试在此处总结各种答案的内容,更新至PowerShell 5:

  • 如果您只使用 PowerShell 3 或更高版本,请使用 $PSCommandPath

  • 如果想要与旧版本兼容,请插入shim:

    if ($PSCommandPath -eq $null) { function GetPSCommandPath() { return $MyInvocation.PSCommandPath; } $PSCommandPath = GetPSCommandPath }

    这将添加$PSCommandPath(如果它不存在)。

    Shim代码可以在任何地方执行(顶层或函数内部),但$PSCommandPath变量受常规作用域规则的约束(例如,如果您将shim放在函数中,则该变量仅限于该函数)。

详细信息

各种答案中使用了4种不同的方法,因此我编写了此脚本以演示每种方法(以及$PSCommandPath):

function PSCommandPath() { return $PSCommandPath }
function ScriptName() { return $MyInvocation.ScriptName }
function MyCommandName() { return $MyInvocation.MyCommand.Name }
function MyCommandDefinition() {
    # Begin of MyCommandDefinition()
    # Note: ouput of this script shows the contents of this function, not the execution result
    return $MyInvocation.MyCommand.Definition
    # End of MyCommandDefinition()
}
function MyInvocationPSCommandPath() { return $MyInvocation.PSCommandPath }

Write-Host ""
Write-Host "PSVersion: $($PSVersionTable.PSVersion)"
Write-Host ""
Write-Host "`$PSCommandPath:"
Write-Host " *   Direct: $PSCommandPath"
Write-Host " * Function: $(PSCommandPath)"
Write-Host ""
Write-Host "`$MyInvocation.ScriptName:"
Write-Host " *   Direct: $($MyInvocation.ScriptName)"
Write-Host " * Function: $(ScriptName)"
Write-Host ""
Write-Host "`$MyInvocation.MyCommand.Name:"
Write-Host " *   Direct: $($MyInvocation.MyCommand.Name)"
Write-Host " * Function: $(MyCommandName)"
Write-Host ""
Write-Host "`$MyInvocation.MyCommand.Definition:"
Write-Host " *   Direct: $($MyInvocation.MyCommand.Definition)"
Write-Host " * Function: $(MyCommandDefinition)"
Write-Host ""
Write-Host "`$MyInvocation.PSCommandPath:"
Write-Host " *   Direct: $($MyInvocation.PSCommandPath)"
Write-Host " * Function: $(MyInvocationPSCommandPath)"
Write-Host ""

输出:

PS C:\> .\Test\test.ps1

PSVersion: 5.1.19035.1

$PSCommandPath:
 *   Direct: C:\Test\test.ps1
 * Function: C:\Test\test.ps1

$MyInvocation.ScriptName:
 *   Direct:
 * Function: C:\Test\test.ps1

$MyInvocation.MyCommand.Name:
 *   Direct: test.ps1
 * Function: MyCommandName

$MyInvocation.MyCommand.Definition:
 *   Direct: C:\Test\test.ps1
 * Function:
    # Begin of MyCommandDefinition()
    # Note this is the contents of the MyCommandDefinition() function, not the execution results
    return $MyInvocation.MyCommand.Definition;
    # End of MyCommandDefinition()


$MyInvocation.PSCommandPath:
 *   Direct:
 * Function: C:\Test\test.ps1

注意事项:

  • 脚本在C:\目录下执行,但实际脚本路径为C:\Test\test.ps1
  • 没有方法能告诉你传递的调用路径(.\Test\test.ps1
  • $PSCommandPath是唯一可靠的方法,但是它是在PowerShell 3中引入的
  • 对于版本低于3的情况,没有单一的方法可以在函数内外同时使用

8
对于今天(2017年)的任何读者来说,他们应该阅读这篇帖子作为正确答案!+1 - Collin Chaffin
2
@CollinChaffin:同意,现在(2017年)最不受支持的是Windows 7,因此如果不需要遗留(Windows XP),就没有理由不使用$PSCommandPath - tukan
第一个代码示例有缺陷,因为它包含了同一函数的两个定义(function PSCommandPath),以及对错误函数的引用(Write-Host " * Direct: $PSCommandPath"; Write-Host " * Function: $(ScriptName)";) - 或者我是否忽略了某些明显的东西? - Mike L'Angelo
@MikeL'Angelo 你说得对!这个问题被忽视了3年。已经修复,谢谢。不过输出和结论仍然是一样的。 - gregmac
1
一个长答案,其中$PSCommandPath在99%的情况下就足够了。不过还是谢谢。 - Timo
我手头没有 PS<3, 但是如果回退到 if ($PSCommandPath -eq $null) { $PSCommandPath = $MyInvocation.PSCommandPath; },是否就足够了呢? - Nuno André

87

尽管目前的答案在大多数情况下是正确的,但在某些情况下它将无法给出正确的答案。如果您在脚本内使用函数,则:

$MyInvocation.MyCommand.Name 
返回函数的名称而不是脚本名称的名称。
function test {
    $MyInvocation.MyCommand.Name
}

无论你的脚本如何命名,它都会给你“test”。 获取脚本名称的正确命令始终是

$MyInvocation.ScriptName

这将返回您正在执行的脚本的完整路径。如果您只需要脚本文件名,则此代码应该能够帮助您:

这会返回你执行的脚本的完整路径。如果你只需要脚本文件名,那么这段代码可以帮到你:

split-path $MyInvocation.PSCommandPath -Leaf

6
请注意,在Posh v4中,Scriptname在顶层是未定义的。我喜欢在顶层使用$MyInvocation.MyCommand.Definition来获取完整路径或名称,根据其他答案中的方式。 - AnneTheAgile
30
$MyInvocation.ScriptName 对于我来说返回空字符串,PS v3.0。 - JohnC
4
$MyInvocation.ScriptName 只能在函数内部使用。请参考我的下面的回答 - gregmac

76

如果你只需要文件名(而不是完整路径),请使用以下代码:

$ScriptName = $MyInvocation.MyCommand.Name

这对于全局范围之外的任何用途都是错误的。如果您创建一个函数并在函数中调用此代码,则将获得函数的名称而不是文件的名称。 - ashrasmun

34

尝试以下代码:

$path =  $MyInvocation.MyCommand.Definition 

这可能不会给你输入的实际路径,但它会给你一个指向文件的有效路径。


1
@Hamish,问题明确指出如果从文件中调用。 - JaredPar
FYI:这将为您提供完整的路径和文件名(PowerShell 2.0) - Ralph Willgoss
我正在寻找这个命令。谢谢,JaredPar! :) - sqlfool
使用Split-Path获取目录?$path = Split-Path $MyInvocation.MyCommand.Definition -Parent - Underverse

9

注意:

$PSScriptRoot$PSCommandPath自动变量不同,$MyInvocation自动变量的PSScriptRootPSCommandPath属性包含有关调用脚本的信息,而不是当前脚本。

例如:

PS C:\Users\S_ms\OneDrive\Documents> C:\Users\SP_ms\OneDrive\Documents\DPM ...
=!C:\Users\S_ms\OneDrive\Documents\DPM.ps1

...其中DPM.ps1包含以下内容:

Write-Host ("="+($MyInvocation.PSCommandPath)+"!"+$PSCommandPath)

8
如果您正在寻找脚本所在的当前目录,可以尝试以下方法:
$fullPathIncFileName = $MyInvocation.MyCommand.Definition
$currentScriptName = $MyInvocation.MyCommand.Name
$currentExecutingPath = $fullPathIncFileName.Replace($currentScriptName, "")

Write-Host $currentExecutingPath

1
那在 C:\ilike.ps123\ke.ps1 上运行不正确,对吧? - fridojet
@fridojet - 不确定,我不在附近的PS终端测试它。为什么你不试试看呢? - Ryk
不,只是个修辞问题;-) ——因为 Replace() 方法会替换 每一个 指定的字符串(而不仅仅是最后一个),我已经测试过了,所以这只是合乎逻辑而已。然而,在字符串上执行减法运算是个好主意。 - fridojet
String.TrimEnd() ($currentExecutingPath = $fullPathIncFileName.TrimEnd($currentScriptName)) жШѓжАОдєИж†ЈзЪДеСҐпЉЯеЃГиГље§Яж≠£еЄЄеЈ•дљЬпЉЪ "Ich bin Hamster".TrimEnd("ster") ињФеЫЮ Ich bin HamпЉМиАМ "Ich bin Hamsterchen".TrimEnd("ster") ињФеЫЮ Ich bin HamsterchenпЉИиАМдЄНжШѓ Ich bin HamchenпЉЙ- еЊИе•љпЉБ - fridojet
`$currentScriptPath = $MyInvocation.MyCommand.Definition;$currentScriptName = $MyInvocation.MyCommand.Name;$currentScriptDir = $currentScriptPath.Substring(0,$currentScriptPath.IndexOf($currentScriptName));` - Y P
或者简单地使用 [System.IO.Path]::GetDirectoryName($MyInvocation.MyCommand.Definition) - Jozef Benikovský

7

正如之前的回答中所提到的,使用"$MyInvocation"存在作用域问题,并不一定提供一致的数据(返回值与直接访问值)。我发现,获取脚本路径、名称、参数、命令行等脚本信息的“最干净”(最一致)方法是在“MyInvocation”上使用“Get-Variable”,无论在主函数或后续/嵌套函数调用中都可以。

# Get the MyInvocation variable at script level
# Can be done anywhere within a script
$ScriptInvocation = (Get-Variable MyInvocation -Scope Script).Value

# Get the full path to the script
$ScriptPath = $ScriptInvocation.MyCommand.Path

# Get the directory of the script
$ScriptDirectory = Split-Path $ScriptPath

# Get the script name
# Yes, could get via Split-Path, but this is "simpler" since this is the default return value
$ScriptName = $ScriptInvocation.MyCommand.Name

# Get the invocation path (relative to $PWD)
# @GregMac, this addresses your second point
$InvocationPath = ScriptInvocation.InvocationName

所以,您可以获得与$PSCommandPath相同的信息,但是在这个过程中还可以获得更多信息。不确定,但似乎"Get-Variable"直到PS3才可用,因此对于旧系统(未更新)没有太多帮助。
使用"-Scope"时也有一些有趣的方面,您可以回溯以获取调用函数的名称等。0=当前,1=父级等。
希望这有点有帮助。
参考:https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/get-variable

5

我认为有一种更好的方法,可以通过设置变量$MyInvocation.MyCommand.Path的作用域来实现:

例如:$script:MyInvocation.MyCommand.Name

这种方法在所有调用情况下都有效:

例如: Somescript.ps1

function printme () {
    "In function:"
    ( "MyInvocation.ScriptName: " + [string]($MyInvocation.ScriptName) )
    ( "script:MyInvocation.MyCommand.Name: " + [string]($script:MyInvocation.MyCommand.Name) )
    ( "MyInvocation.MyCommand.Name: " + [string]($MyInvocation.MyCommand.Name) )
}
"Main:"
( "MyInvocation.ScriptName: " + [string]($MyInvocation.ScriptName) )
( "script:MyInvocation.MyCommand.Name: " + [string]($script:MyInvocation.MyCommand.Name) )
( "MyInvocation.MyCommand.Name: " + [string]($MyInvocation.MyCommand.Name) )
" "
printme
exit

输出:

PS> powershell C:\temp\test.ps1
Main:
MyInvocation.ScriptName:
script:MyInvocation.MyCommand.Name: test.ps1
MyInvocation.MyCommand.Name: test.ps1

In function:
MyInvocation.ScriptName: C:\temp\test.ps1
script:MyInvocation.MyCommand.Name: test.ps1
MyInvocation.MyCommand.Name: printme

请注意上面被接受的答案在从主函数中调用时不返回任何值。此外,需要注意的是,当问题只要求脚本名称时,上述被接受的答案会返回完整路径。这个作用域变量可以在任何地方使用。
如果你确实需要完整路径,那么只需调用:
$script:MyInvocation.MyCommand.Path

4

以下是@gregmac(出色且详尽)回答的简短演示,基本上建议在使用Powershell 3.0及以上版本时仅使用$PSCommandPath命令来返回当前正在运行的脚本。

这里展示了返回完整路径或仅文件名的方式。

Test.ps1:

'Direct:'
$PSCommandPath  # Full Path 
Split-Path -Path $PSCommandPath -Leaf  # File Name only

function main () {
  ''
  'Within a function:'
  $PSCommandPath
  Split-Path -Path $PSCommandPath -Leaf
}

main

输出:

PS> .\Test.ps1
Direct:
C:\Users\John\Documents\Sda\Code\Windows\PowerShell\Apps\xBankStatementRename\Test.ps1
Test.ps1

Within a function:
C:\Users\John\Documents\Sda\Code\Windows\PowerShell\Apps\xBankStatementRename\Test.ps1
Test.ps1

2

我用以下脚本进行了一些测试,分别在PS 2和PS 4上测试,结果相同。希望这能帮助大家。

$PSVersionTable.PSVersion
function PSscript {
  $PSscript = Get-Item $MyInvocation.ScriptName
  Return $PSscript
}
""
$PSscript = PSscript
$PSscript.FullName
$PSscript.Name
$PSscript.BaseName
$PSscript.Extension
$PSscript.DirectoryName

""
$PSscript = Get-Item $MyInvocation.InvocationName
$PSscript.FullName
$PSscript.Name
$PSscript.BaseName
$PSscript.Extension
$PSscript.DirectoryName

结果 -

Major  Minor  Build  Revision
-----  -----  -----  --------
4      0      -1     -1      

C:\PSscripts\Untitled1.ps1
Untitled1.ps1
Untitled1
.ps1
C:\PSscripts

C:\PSscripts\Untitled1.ps1
Untitled1.ps1
Untitled1
.ps1
C:\PSscripts

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接