web-dev-qa-db-ja.com

PowerShellでパスを正規化する方法は?

私には2つのパスがあります。

fred\frog

そして

..\frag

PowerShellで次のようにそれらを結合できます。

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

それは私にこれを与えます:

fred\frog\..\frag

しかし、私はそれを望んでいません。次のように、二重ドットのない正規化されたパスが必要です。

fred\frag

どうすれば入手できますか?

82
dan-gph

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
_

個人的にも複雑ではありません、私はこのために外部スクリプトに依存するソリューションを軽daします、それは_Join-Path_とpwdGetFullPathが単にそれを作ることでかなり適切に解決される単純な問題ですかなり)。 相対部分のみのみを保持する場合は、.Substring((pwd).Path.Trim('\').Length + 1)と出来上がりを追加するだけです!

_fred\frag
_

更新

_C:\_ Edgeケースを指摘してくれた@Dangphに感謝します。

75
John Leidegren

Resolve-pathを使用して、..\fragをフルパスに展開できます。

PS > resolve-path ..\frag 

Compose()メソッドを使用してパスを正規化してみてください:

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

Path.GetFullPath を使用することもできますが、(ダンRの答えのように)これはパス全体を提供します。使用方法は次のとおりです。

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

またはより興味深いことに

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

どちらも次のようになります(現在のディレクトリがD:\であると仮定):

D:\fred\frag

このメソッドは、fredまたはfragが実際に存在するかどうかを判断しようとしないことに注意してください。

23
Charlie

受け入れられた答えは大きな助けでしたが、絶対パスも適切に「正規化」しません。以下に、絶対パスと相対パスの両方を正規化する派生物を示します。

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;
}
17
Sean Hanna

PowerShellのプロバイダーモデルでは、PowerShellの現在のパスをWindowsがプロセスの作業ディレクトリと見なすものと異なるため、PowerShell以外のパス操作関数(System.IO.Path内の関数など)はPowerShellから信頼できません。

また、既に発見したかもしれませんが、PowerShellのResolve-PathおよびConvert-Pathコマンドレットは、相対パス(「..」を含むもの)をドライブ修飾された絶対パスに変換するのに役立ちますが、参照されるパスが存在しない場合は失敗します。

次の非常に単純なコマンドレットは、存在しないパスに対して機能するはずです。 「fred」または「frag」ファイルまたはフォルダーが見つからない場合でも、「fred\frog\..\frag」を「d:\ 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($_)
        }
    }
}
10

このライブラリは優れています: NDepend.Helpers.FileDirectoryPath

EDIT:これは私が思いついたものです:

[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へのパスが必要であることに注意してください。現在実行中のスクリプトを含むフォルダーを見つけるために使用できるトリックがありますが、私の場合、使用できる環境変数があったので、それを使用しました。

3
dan-gph

関数を作成します。この関数は、システムに存在しないパスを正規化し、ドライブ文字を追加しません。

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'

RegExの支援についてOliver Schadlichに感謝します。

2
M.Hubers

これにより、完全なパスが得られます。

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

これにより、現在のディレクトリからの相対パスが得られます。

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

何らかの理由で、fragdirectoryではなくファイルである場合にのみ機能します。

1
dan-gph

パスに修飾子(ドライブ文字)が含まれている場合、- Powershellへのx0nの回答:存在しない可能性のあるパスを解決しますか? はパスを正規化します。パスに修飾子が含まれていない場合、パスは正規化されますが、現在のディレクトリに関連する完全修飾パスが返されますが、これは必要なものではない場合があります。

$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
1
WileCau

さて、1つの方法は次のとおりです。

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

待って、多分私は質問を誤解しています。あなたの例では、fragはカエルのサブフォルダーですか?

0
EBGreen

..部分を削除する必要がある場合は、System.IO.DirectoryInfoオブジェクトを使用できます。コンストラクターで 'fred\frog ..\frag'を使用します。 FullNameプロパティは、正規化されたディレクトリ名を提供します。

唯一の欠点は、完全なパス(c:\ test\fred\fragなど)を提供することです。

0
Dan R

ここでコメントの適切な部分を組み合わせて、相対パスと絶対パスを統合しました。

[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
0
TNT