如何在PowerShell中规范化路径?

时间:2009-01-30 14:15:34

标签: powershell path

我有两条路:

fred\frog

..\frag

我可以像这样在PowerShell中加入它们:

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

这给了我这个:

fred\frog\..\frag

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

fred\frag

我怎么能得到它?

12 个答案:

答案 0 :(得分:96)

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

PS > resolve-path ..\frag 

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

[io.path]::Combine("fred\frog",(resolve-path ..\frag).path)

答案 1 :(得分:72)

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

由于cdSet-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-PathpwdGetFullPath恰好解决的简单问题(.Substring((pwd).Path.Trim('\').Length + 1)只是为了制作它很漂亮)。如果您只想保留仅相关部分,则只需添加fred\frag 即可!

C:\

更新

感谢@Dangph指出{{1}}边缘情况。

答案 2 :(得分:22)

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

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

或者更有趣的是

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

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

D:\fred\frag

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

答案 3 :(得分:16)

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

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;
}

答案 4 :(得分:10)

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

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

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

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

    process {
        $Path | ForEach-Object {
            $PSCmdlet.SessionState.Path.GetUnresolvedProviderPathFromPSPath($_)
        }
    }
}

答案 5 :(得分: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的路径。有一个技巧可以用来找到包含当前正在执行的脚本的文件夹,但在我的情况下,我有一个我可以使用的环境变量,所以我只是使用它。

答案 6 :(得分:2)

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

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中寻求帮助。

答案 7 :(得分:1)

这给出了完整的路径:

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

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

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

出于某种原因,只有当frag是文件而不是directory时,它们才有效。

答案 8 :(得分:1)

如果路径包含限定符(驱动器号),则x0n's answer to Powershell: resolve path that might not exist?将对路径进行规范化。如果该路径不包含限定符,它仍将被规范化,但将返回相对于当前目录的完全限定路径,这可能不是您想要的。

$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

答案 9 :(得分:0)

嗯,一种方法是:

Join-Path 'fred\frog' '..\frag'.Replace('..', '')
等等,也许我误解了这个问题。在你的例子中,是否是青蛙的子文件夹?

答案 10 :(得分:0)

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

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

答案 11 :(得分: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