如何获得正在执行的cmdlet的当前目录

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

488365 次浏览

可靠的方法就像你展示的$MyInvocation.MyCommand.Path一样。

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

PowerShell v3 +:

使用自动变量$PSScriptRoot

Path通常为空。这个功能比较安全。

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

你会认为使用'。\'作为路径意味着它是调用路径。但也不是所有时候。例如,如果您在作业ScriptBlock中使用它。在这种情况下,它可能指向%profile%\Documents。

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

(Get-Item .).FullName

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

 $PSScriptRoot

about_Automatic_Variablesabout_Scripts都表示:

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

我是这样使用的:

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

你还可以使用:

(Resolve-Path .\).Path

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

(PowerShell 2.0起可用。)

我喜欢一行解决方案:)

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

试一试:

(Get-Location).path

或者:

($pwd).path

为了扩展@Cradle的答案:你也可以写一个多用途功能,它会给你每个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
}


}

Get-Location将返回当前位置:

$Currentlocation = Get-Location

试试这个:

$WorkingDir = Convert-Path .

在Powershell 3及以上,您可以简单地使用

$PSScriptRoot

不管怎样,作为一个单行的解决方案,下面是一个对我来说有效的解决方案。

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

最后的1是忽略/

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

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

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

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

输出

MyWorkingDirectory

我也有类似的问题,这给我带来了很多麻烦,因为我正在用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更新)中工作

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

在以下ide中调试时,大多数答案都不能工作:

  • PS-ISE (PowerShell ISE)
  • VS Code (Visual Studio Code)

因为在这些情况下,$PSScriptRoot是空的,而Resolve-Path .\(和类似的)将导致不正确的路径。

Freakydinde的回答是唯一解决这些情况的,所以我投票赞成,但我不认为答案中的Set-Location是真正需要的。所以我修正了这个问题,让代码更清晰一些:

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

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

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

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

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

我的是一个短,所以拔掉所有的USB从它和重新编译