这应该是一个简单的任务,但是我已经看到过几次关于如何获取已执行cmdlet所在目录的路径的尝试,结果好坏参半。例如,当我执行C:\temp\myscripts\mycmdlet。我想能够将C:\temp\myscripts存储在mycmdlet.ps1中的一个变量中。

这是一个有效的解决方案(尽管有点麻烦):

$invocation = (Get-Variable MyInvocation).Value
$directorypath = Split-Path $invocation.MyCommand.Path
$settingspath = $directorypath + '\settings.xml'

另一个人建议这样的解决方案,它只适用于我们的测试环境:

$settingspath = '.\settings.xml'

我非常喜欢后一种方法,与每次都将文件路径解析为参数相比,我更喜欢这种方法,但我无法让它在我的开发环境中工作。我该怎么办?这与PowerShell的配置方式有关吗?


可靠的方法就像您显示的$ myinvoke . 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_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起可用。)


我喜欢单行解决方案:)

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

试一试:

(Get-Location).path

or:

($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


I had similar problems and it made me a lot of trouble since I am making programs written in PowerShell (full end user GUI applications) and I have a lot of files and resources I need to load from disk. From my experience, using . to represent current directory is unreliable. It should represent current working directory, but it often does not. It appears that PowerShell saves location from which PowerShell has been invoked inside .. To be more precise, when PowerShell is first started, it starts, by default, inside your home user directory. That is usually directory of your user account, something like C:\USERS\YOUR USER NAME. After that, PowerShell changes directory to either directory from which you invoked it, or to directory where script you are executing is located before either presenting you with PowerShell prompt or running the script. But that happens after PowerShell app itself originally starts inside your home user directory.

And . represents that initial directory inside which PowerShell started. So . only represents current directory in case if you invoked PowerShell from the wanted directory. If you later change directory in PowerShell code, change appears not to be reflected inside . in every case. In some cases . represents current working directory, and in others directory from which PowerShell (itself, not the script) has been invoked, what can lead to inconsistent results. For this reason I use invoker script. PowerShell script with single command inside: POWERSHELL. That will ensure that PowerShell is invoked from the wanted directory and thus make . represent current directory. But it only works if you do not change directory later in PowerShell code. In case of a script, I use invoker script which is similar to last one I mentioned, except it contains a file option: POWERSHELL -FILE DRIVE:\PATH\SCRIPT NAME.PS1. That ensures that PowerShell is started inside current working directory.

只需单击脚本就可以从主用户目录调用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从它和重新编译