109

我有两条路:

fred\frog

..\frag

我可以像这样在 PowerShell 中将它们连接在一起:

join-path 'fred\frog' '..\frag'

这给了我这个:

fred\frog\..\frag

但我不想那样。我想要一个没有双点的标准化路径,如下所示:

fred\frag

我怎么能得到那个?

4

12 回答 12

110

您可以使用 resolve-path 将 ..\frag 扩展到其完整路径:

PS > resolve-path ..\frag 

尝试使用 combine() 方法规范化路径:

[io.path]::Combine("fred\frog",(resolve-path ..\frag).path)
于 2009-01-31T16:25:10.000 回答
89

您可以使用$pwd,Join-Path和的组合[System.IO.Path]::GetFullPath来获得完全限定的扩展路径。

由于cd( Set-Location) 不会更改进程的当前工作目录,因此只需将相对文件名传递给不理解 PowerShell 上下文的 .NET API,可能会产生意想不到的副作用,例如解析为基于初始工作的路径目录(不是您当前的位置)。

你要做的是首先限定你的路径:

Join-Path (Join-Path $pwd fred\frog) '..\frag'

这产生(给定我当前的位置):

C:\WINDOWS\system32\fred\frog\..\frag

有了绝对基础,现在可以安全地调用 .NET API GetFullPath

[System.IO.Path]::GetFullPath((Join-Path (Join-Path $pwd fred\frog) '..\frag'))

这为您提供了完全合格的路径,并..正确解析:

C:\WINDOWS\system32\fred\frag

就个人而言,这也不复杂,我不屑于为此依赖外部脚本的解决方案,这是一个简单的问题,而通过Join-Pathand $pwd(GetFullPath只是为了让它漂亮) 解决了这个问题。如果您只想保留相关部分,您只需添加.Substring($pwd.Path.Trim('\').Length + 1)并瞧!

fred\frag

更新

感谢@Dangph 指出了极端C:\情况。

于 2012-12-12T19:39:35.883 回答
26

您也可以使用Path.GetFullPath,尽管(与 Dan R 的回答一样)这将为您提供整个路径。用法如下:

[IO.Path]::GetFullPath( "fred\frog\..\frag" )

或者更有趣的是

[IO.Path]::GetFullPath( (join-path "fred\frog" "..\frag") )

两者都会产生以下结果(假设您的当前目录是 D:\):

D:\fred\frag

请注意,此方法不会尝试确定 fred 或 frag 是否实际存在。

于 2009-01-30T15:22:06.447 回答
24

接受的答案是一个很大的帮助,但是它也不能正确地“规范化”绝对路径。在下面找到我的衍生作品,它标准化了绝对和相对路径。

function Get-AbsolutePath ($Path)
{
    # System.IO.Path.Combine has two properties making it necesarry here:
    #   1) correctly deals with situations where $Path (the second term) is an absolute path
    #   2) correctly deals with situations where $Path (the second term) is relative
    # (join-path) commandlet does not have this first property
    $Path = [System.IO.Path]::Combine( ((pwd).Path), ($Path) );

    # this piece strips out any relative path modifiers like '..' and '.'
    $Path = [System.IO.Path]::GetFullPath($Path);

    return $Path;
}
于 2013-06-06T14:18:24.543 回答
14

任何非 PowerShell 路径操作函数(例如 System.IO.Path 中的那些)在 PowerShell 中都不可靠,因为 PowerShell 的提供程序模型允许 PowerShell 的当前路径与 Windows 认为的进程工作目录不同。

此外,您可能已经发现,PowerShell 的 Resolve-Path 和 Convert-Path cmdlet 对于将相对路径(包含 '..' 的那些)转换为驱动器限定的绝对路径很有用,但如果引用的路径不存在,它们会失败。

以下非常简单的 cmdlet 应该适用于不存在的路径。即使找不到“fred”或“frag”文件或文件夹(并且当前的 PowerShell 驱动器为“d:”),它也会将“fred\frog\..\frag”转换为“d:\fred\frag” .

function Get-AbsolutePath {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [string[]]
        $Path
    )

    process {
        $Path | ForEach-Object {
            $PSCmdlet.SessionState.Path.GetUnresolvedProviderPathFromPSPath($_)
        }
    }
}
于 2011-02-13T02:51:55.270 回答
4

如果路径包含限定符(驱动器号),那么x0n 对 Powershell 的回答:解析可能不存在的路径?将规范化路径。如果路径不包含限定符,它仍然会被规范化,但会返回相对于当前目录的完全限定路径,这可能不是您想要的。

$p = 'X:\fred\frog\..\frag'
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($p)
X:\fred\frag

$p = '\fred\frog\..\frag'
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($p)
C:\fred\frag

$p = 'fred\frog\..\frag'
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($p)
C:\Users\WileCau\fred\frag
于 2018-09-04T02:15:54.317 回答
3

这个库很好:NDepend.Helpers.FileDirectoryPath

编辑:这就是我想出的:

[Reflection.Assembly]::LoadFrom("path\to\NDepend.Helpers.FileDirectoryPath.dll") | out-null

Function NormalizePath ($path)
{
    if (-not $path.StartsWith('.\'))  # FilePathRelative requires relative paths to begin with '.'
    {
        $path = ".\$path"
    }

    if ($path -eq '.\.')  # FilePathRelative can't deal with this case
    {
        $result = '.'
    }
    else
    {
        $relPath = New-Object NDepend.Helpers.FileDirectoryPath.FilePathRelative($path)
        $result = $relPath.Path
    }

    if ($result.StartsWith('.\')) # remove '.\'. 
    {
        $result = $result.SubString(2)
    }

    $result
}

像这样称呼它:

> NormalizePath "fred\frog\..\frag"
fred\frag

请注意,此代码段需要 DLL 的路径。您可以使用一个技巧来查找包含当前正在执行的脚本的文件夹,但在我的情况下,我有一个可以使用的环境变量,所以我只是使用了它。

于 2009-02-28T05:05:50.457 回答
1

这给出了完整路径:

(gci 'fred\frog\..\frag').FullName

这给出了相对于当前目录的路径:

(gci 'fred\frog\..\frag').FullName.Replace((gl).Path + '\', '')

出于某种原因,它们仅frag在文件而不是directory.

于 2009-01-31T01:46:27.417 回答
1

创建一个函数。此功能将规范化系统上不存在的路径以及不添加驱动器号。

function RemoveDotsInPath {
  [cmdletbinding()]
  Param( [Parameter(Position=0,  Mandatory=$true)] [string] $PathString = '' )

  $newPath = $PathString -creplace '(?<grp>[^\n\\]+\\)+(?<-grp>\.\.\\)+(?(grp)(?!))', ''
  return $newPath
}

前任:

$a = 'fooA\obj\BusinessLayer\..\..\bin\BusinessLayer\foo.txt'
RemoveDotsInPath $a
'fooA\bin\BusinessLayer\foo.txt'

感谢 Oliver Schadlich 在 RegEx 方面的帮助。

于 2012-07-20T22:57:03.023 回答
0

如果您需要摆脱 .. 部分,您可以使用 System.IO.DirectoryInfo 对象。在构造函数中使用“fred\frog..\frag”。FullName 属性将为您提供规范化的目录名称。

唯一的缺点是它会给你完整的路径(例如c:\test\fred\frag)。

于 2009-01-30T14:37:06.927 回答
0

此处注释的权宜之计部分结合在一起,使它们统一了相对路径和绝对路径:

[System.IO.Directory]::SetCurrentDirectory($pwd)
[IO.Path]::GetFullPath($dapath)

一些样本:

$fps = '.', 'file.txt', '.\file.txt', '..\file.txt', 'c:\somewhere\file.txt'
$fps | % { [IO.Path]::GetFullPath($_) }

输出:

C:\Users\thelonius\tests
C:\Users\thelonius\tests\file.txt
C:\Users\thelonius\tests\file.txt
C:\Users\thelonius\file.txt
c:\somewhere\file.txt
于 2018-02-23T18:52:03.243 回答
-1

好吧,一种方法是:

Join-Path 'fred\frog' '..\frag'.Replace('..', '')

等等,也许我误解了这个问题。在您的示例中,frag 是青蛙的子文件夹吗?

于 2009-01-30T14:26:08.960 回答