2009-01-30 96 views
70

我有两条路:如何规范PowerShell中的路径?

fred\frog 

..\frag 

我可以在PowerShell中一起加入他们这样的:

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

这给了我这样的:

fred\frog\..\frag 

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

fred\frag 

我该如何得到它?

+1

是frag是青蛙的子文件夹?如果没有,那么结合路径会让你fred \ frog \ frag。如果这是一个非常不同的问题。 “ – EBGreen 2009-01-30 14:41:46

回答

59

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

由于cdSet-Location)不会改变进程当前工作目录,只是传递一个相对的文件名到.NET API不理解PowerShell的情况下,可以产生意想不到的副作用,如解决的路径基于最初的工作目录(而不是您当前的位置)。

你要做的就是首先符合您的路径:

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 

UPDATE

由于@Dangph用于指出了C:\边缘情况。

0

那么,一个办法是:

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

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

+0

”是frag的青蛙子文件夹吗?“不。..意味着升一级。 frag是fred中的子文件夹(或文件)。 – 2009-01-31 01:48:33

0

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

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

19

你也可以使用Path.GetFullPath,虽然(与丹R的答案一样),这会给你整个路径。用法是如下:

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

或更有趣

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

两者产生以下(假设你的当前目录是d:\):

D:\fred\frag 

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

+0

接近了,但是当我尝试它时,即使我当前的目录是“C:\ scratch”,也会得到“H:\ fred \ frag”,这是错误的。 (根据MSDN,它不应该这样做。)但是,它给了我一个想法。我将添加它作为答案。 – 2009-01-31 01:37:50

+8

你的问题是你需要在.NET中设置当前目录。 `[System.IO.Directory] ​​:: SetCurrentDirectory(((Get-Location-PSProvider FileSystem).ProviderPath))` – JasonMArcher 2011-08-16 04:47:00

+2

只需明确声明:`[IO.Path] :: GetFullPath()`,与PowerShell本地` “Resolve-Path”也适用于不存在的路径。它的缺点是需要首先同步.NET的工作文件夹,正如@JasonMArcher指出的那样。 – mklement0 2013-03-11 23:45:45

1

这使的完整路径:

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

此路径相对于给到当前目录:

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

出于某种原因,他们只工作,如果frag是一个文件,而不是directory

+1

gci是get-childitem的别名。目录的子项是其内容。用gi替换gci,它应该适用于两者。 – zdan 2009-01-31 02:43:02

+2

Get-Item很好地工作。但是,这种方法同样需要存在文件夹。 – 2012-05-15 09:58:34

92

您还可以扩大.. \断枝它与解决路径完整路径:

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

这个库是好的:

PS > resolve-path ..\frag 

尝试使用联合收割机()方法正常化的路径: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。有一个技巧可以用来查找包含当前正在执行的脚本的文件夹,但在我的情况下,我有一个我可以使用的环境变量,所以我只是用它。

9

任何非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($_) 
     } 
    } 
} 
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' 

要感谢的是奥利弗Schadlich在正则表达式的帮助。

13

接受的答案是一个很大的帮助,但它并没有适当地“规范化”一条绝对路径。在下面找到我的派生工作,它将绝对和相对路径归一化。

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