这应该是一个简单的任务,但我已经看到了几次尝试如何获取执行的 cmdlet 所在目录的路径,但成功率参差不齐。例如,当我执行在 C:\temp\myscripts\settings.xml
有一个设置文件的 C:\temp\myscripts\mycmdlet.ps1
时,我希望能够将 C:\temp\myscripts
存储在 mycmdlet.ps1
内的变量中。
这是一种可行的解决方案(虽然有点麻烦):
$invocation = (Get-Variable MyInvocation).Value
$directorypath = Split-Path $invocation.MyCommand.Path
$settingspath = $directorypath + '\settings.xml'
另一个人建议这个解决方案只适用于我们的测试环境:
$settingspath = '.\settings.xml'
我非常喜欢后一种方法,并且更喜欢它每次都必须将文件路径解析为参数,但我无法让它在我的开发环境中工作。我应该怎么办?它与PowerShell的配置方式有关吗?
是的,这应该有效。但是,如果您需要查看绝对路径,这就是您所需要的:
(Get-Item .).FullName
执行此操作的可靠方法就像您展示的 $MyInvocation.MyCommand.Path
。
使用相对路径将基于 $pwd、在 PowerShell 中、应用程序的当前目录或 .NET API 的当前工作目录。
PowerShell v3+:
使用自动变量 $PSScriptRoot
。
$PSScriptRoot
和 $MyInvocation.MyCommand.Path
之间的区别吗?
$MyInvocation.MyCommand | gm
? Path
是脚本文件的全路径,直接在终端执行是找不到的。
$MyInvocation.MyCommand.Path\File.csv
& 时遇到了问题在我用谷歌搜索 $MyInvocation.MyCommand.Path
后,我发现了这个 MS article 的详细信息,我不得不将其中的一部分拆分出来 &在我让它工作之前放入变量: $myDir = Split-Path -Parent $MyInvocation.MyCommand.Path
。对powershell非常新,所以问题可能是我缺乏理解
最简单的方法似乎是使用以下预定义变量:
$PSScriptRoot
about_Automatic_Variables
和 about_Scripts
都表示:
在 PowerShell 2.0 中,此变量仅在脚本模块 (.psm1) 中有效。从 PowerShell 3.0 开始,它在所有脚本中都有效。
我这样使用它:
$MyFileName = "data.txt"
$filebase = Join-Path $PSScriptRoot $MyFileName
您还可以使用:
(Resolve-Path .\).Path
括号中的部分返回一个 PathInfo
对象。
(自 PowerShell 2.0 起可用。)
C:\mydir
,并且我调用命令 C:\dir1\dir2\dir3\mycmdlet.ps1
,那么这将解析为 C:\mydir
,而不是 C:\dir1\dir2\dir3
。调用新的可执行文件也有同样的问题,因为当前目录是从父进程继承的。
尝试 :
(Get-Location).path
或者:
($pwd).path
$pwd
/$PWD
!更有用的IMO...
路径通常为空。这个功能更安全。
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("\"));
}
}
Get-Location
将返回当前位置:
$Currentlocation = Get-Location
我喜欢the one-line solution :)
$scriptDir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
尝试这个:
$WorkingDir = Convert-Path .
在 Powershell 3 及更高版本中,您可以简单地使用
$PSScriptRoot
如果您只需要当前目录的名称,则可以执行以下操作:
((Get-Location) | Get-Item).Name
假设您在 C:\Temp\Location\MyWorkingDirectory> 工作
输出
我的工作目录
在以下 IDE 中调试时,大多数答案都不起作用:
PS-ISE(PowerShell ISE)
VS 代码(Visual Studio 代码)
因为在那些 $PSScriptRoot
是空的并且 Resolve-Path .\
(和类似的)将导致不正确的路径。
Freakydinde's answer 是解决这些情况的唯一一个,所以我对此投了赞成票,但我认为该答案中的 Set-Location
并不是真正需要的。所以我解决了这个问题并使代码更清晰:
$directorypath = if ($PSScriptRoot) { $PSScriptRoot } `
elseif ($psise) { split-path $psise.CurrentFile.FullPath } `
elseif ($psEditor) { split-path $psEditor.GetEditorContext().CurrentFile.Path }
对于它的价值,作为单行解决方案,以下是我的工作解决方案。
$currFolderName = (Get-Location).Path.Substring((Get-Location).Path.LastIndexOf("\")+1)
末尾的 1 是忽略 /
。
感谢上面使用 Get-Location cmdlet 的帖子。
此函数将提示位置设置为脚本路径,处理在 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 }
}
你会认为使用'.\'作为路径意味着它是调用路径。但并非总是如此。例如,如果您在作业 ScriptBlock 中使用它。在这种情况下,它可能指向 %profile%\Documents。
这就是我想出的。它是一个数组,包含多种查找路径的方法,使用当前位置,过滤掉空\空结果,并返回第一个非空值。
@((
($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)
要扩展 @Cradle 的答案:您还可以编写一个 multi-purpose function 来根据 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
}
}
我遇到了类似的问题,这给我带来了很多麻烦,因为我正在制作用 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 新)。
不定期副业成功案例分享
C:\mydir
,并且我调用命令C:\dir1\dir2\dir3\mycmdlet.ps1
,那么这将解析为C:\mydir
,而不是C:\dir1\dir2\dir3
。调用新的可执行文件也有同样的问题,因为当前目录是从父进程继承的。