2009-01-30 42 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