bestsource

PowerShell에서 경로를 정규화하는 방법

bestsource 2023. 4. 19. 23:19
반응형

PowerShell에서 경로를 정규화하는 방법

두 가지 경로가 있습니다.

fred\frog

그리고.

..\frag

다음과 같이 PowerShell에 함께 가입할 수 있습니다.

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

그 결과 알 수긍할 수 있습니다.

fred\frog\..\frag

하지만 난 그걸 원하지 않아.다음과 같이 이중 점이 없는 정규화된 경로를 원합니다.

fred\frag

어떻게 구할 수 있죠?

확장할 수 있습니다.\resolve-path를 사용하여 풀경로로 이동합니다.

PS > resolve-path ..\frag 

combine() 메서드를 사용하여 경로를 정규화해 봅니다.

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

'어울리다'를 할 수 요.$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 APIGetFullPath:

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

경로를 알 수 ...다음 중 하나:

C:\WINDOWS\system32\fred\frag

저는 요.그것은 그냥 입니다.이 문제는 간단한 문제이기 때문에 적절하게 해결됩니다.Join-Path ★★★★★★★★★★★★★★★★★」$pwd )GetFullPath예쁘게 하기 위해서입니다.)상대적인 부분만 유지하려면 다음을 추가합니다..Substring($pwd.Path.Trim('\').Length + 1)★★★★★★★★★★★★★★★★!

fred\frag

갱신하다

@C:\엣지 케이스

받아들여진 답변은 큰 도움이 되었지만 절대적인 경로도 제대로 '정상화'하지 못했다.절대 경로와 상대 경로를 모두 정규화하는 파생 작업을 아래에서 찾아보십시오.

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

경로도 사용할 수 있습니다.GetFullPath. 단, (Dan R의 답변과 마찬가지로) 전체 경로가 제공됩니다.사용 방법은 다음과 같습니다.

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

더 흥미로운 것은

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

둘 다 다음 결과를 생성합니다(현재 디렉토리가 D:\인 경우).

D:\fred\frag

이 메서드는 fred 또는 frag가 실제로 존재하는지 여부를 판별하지 않습니다.

PowerShell 이외의 경로 조작 기능(시스템 내 기능 등)PowerShell의 공급자 모델에서는 PowerShell의 현재 경로가 Windows가 생각하는 프로세스 작업 디렉토리와 다르기 때문에 PowerShell에서는 IO.Path)를 신뢰할 수 없습니다.

또한 PowerShell의 Resolve-Path 및 Convert-Path cmdlet은 상대 경로('..' 포함)를 드라이브 수식 절대 경로로 변환하는 데 유용하지만 참조 경로가 존재하지 않으면 실패합니다.

존재하지 않는 경로에 대해 다음과 같은 매우 단순한 cmdlet이 작동해야 합니다.fred\frog을 변환합니다."fred" 또는 "frag" 파일 또는 폴더를 찾을 수 없는 경우에도 "d:\fred\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($_)
        }
    }
}

경로에 한정자(드라이브 문자)가 포함되어 있는 경우 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

이 라이브러리는 정상입니다: NDepend.도우미.File Directory Path.

편집: 제가 생각해낸 것은 다음과 같습니다.

[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로의 경로가 필요합니다.현재 실행 중인 스크립트가 포함된 폴더를 찾을 때 사용할 수 있는 트릭이 있는데, 제 경우 사용할 수 있는 환경변수가 있어서 그냥 사용했습니다.

경로가 존재하며 절대 경로를 반환해도 상관 없는 경우 를 사용하여-Resolve파라미터:

Join-Path 'fred\frog' '..\frag' -Resolve

이렇게 하면 전체 경로가 제공됩니다.

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

그러면 현재 디렉토리에 상대적인 경로가 제공됩니다.

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

어떤 이유에선지 그들은 오직frag파일이지,directory.

함수를 만듭니다.이 함수는 시스템에 존재하지 않는 경로를 정규화할 뿐만 아니라 드라이브 문자를 추가하지 않습니다.

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에 도움을 주셔서 감사합니다.

다음과 같은 이유로 완전히 받아들일 수 있는 답변은 없습니다.

  • powershell 프로바이더를 지원해야 합니다.
  • 존재하지 않는 드라이브에 존재하지 않는 경로에 대해 작동해야 합니다.
  • ".." 및 "."를 처리해야 합니다. 이것이 정규화된 경로입니다.
  • 외부 라이브러리도 정규식도 없다.
  • 경로를 재루팅할 수 없습니다. 즉, 상대 경로가 상대 경로로 유지됩니다.

다음과 같은 이유로 여기에 나열된 각 방법에 대해 예상되는 결과 목록을 작성했습니다.


function tests {
    context "cwd" {
        it 'has no external libraries' {
            Load-NormalizedPath
        }
        it 'barely work for FileInfos on existing paths' {
            Get-NormalizedPath 'a\..\c' | should -be 'c'
        }
        it 'process .. and . (relative paths)' {
            Get-NormalizedPath 'a\b\..\..\c\.' | should -be 'c'
        }
        it 'must support powershell providers' {
            Get-NormalizedPath "FileSystem::\\$env:COMPUTERNAME\Shared\a\..\c" | should -be "FileSystem::\\$env:COMPUTERNAME\Shared\c"
        }
        it 'must support powershell drives' {
            Get-NormalizedPath 'HKLM:\Software\Classes\.exe\..\.dll' | should -be 'HKLM:\Software\Classes\.dll'
        }
        it 'works with non-existant paths' {
            Get-NormalizedPath 'fred\frog\..\frag\.' | should -be 'fred\frag'
        }
        it 'works with non-existant drives' {
            Get-NormalizedPath 'U:\fred\frog\..\frag\.' | should -be 'U:\fred\frag'
        }
        it 'barely work for direct UNCs' {
            Get-NormalizedPath "\\$env:COMPUTERNAME\Shared\a\..\c" | should -be "\\$env:COMPUTERNAME\Shared\c"
        }
    }
    context "reroot" {
        it 'doesn''t reroot subdir' {
            Get-NormalizedPath 'fred\frog\..\frag\.' | should -be 'fred\frag'
        }
        it 'doesn''t reroot local' {
            Get-NormalizedPath '.\fred\frog\..\frag\.' | should -be 'fred\frag'
        }
        it 'doesn''t reroot parent' {
            Get-NormalizedPath "..\$((Get-Item .).Name)\fred\frog\..\frag\." | should -be 'fred\frag'
        }
    }
    context "drive root" {
        beforeEach { Push-Location 'c:/' }
        it 'works on drive root' {
            Get-NormalizedPath 'fred\frog\..\..\fred\frag\' | should -be 'fred\frag\'
        }
        afterEach { Pop-Location }
    }
    context "temp drive" {
        beforeEach { New-PSDrive -Name temp -PSProvider FileSystem 'b:/tools' }
        it 'works on temp drive' {
            Get-NormalizedPath 'fred\frog\..\..\fred\frag\' | should -be 'fred\frag\'
        }
        it 'works on temp drive with absolute path' {
            Get-NormalizedPath 'temp:\fred\frog\..\..\fred\frag\' | should -be 'temp:\fred\frag\'
        }
        afterEach { Remove-PSDrive -Name temp }
    }
    context "unc drive" {
        beforeEach { Push-Location "FileSystem::\\$env:COMPUTERNAME\Shared\​" }
        it 'works on unc drive' {
            Get-NormalizedPath 'fred\frog\..\..\fred\frag\' | should -be 'fred\frag\'
        }
        afterEach { Pop-Location }
    }
}

정답은 다음과 같습니다.GetUnresolvedProviderPathFromPSPath그 자체로는 동작할 수 없습니다.직접 사용해 보면, 그러한 결과가 나옵니다.이 답변부터http://https://stackoverflow.com/a/52157943/1964796 를 참조해 주세요.

$path = Join-Path '/' $path
$path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($path)
$path = $path.Replace($pwd.Path, '').Replace($pwd.Drive.Root, '')
pros: simple
cons: needs boilerplate to make it correct, doesn't work with other providers or non-ex drives.

 Context cwd
   [+] has no external libraries 4ms (1ms|3ms)
   [+] barely work for FileInfos on existing paths 3ms (2ms|0ms)
   [+] process .. and . (relative paths) 3ms (2ms|0ms)
   [-] must support powershell providers 4ms (3ms|1ms)
    Expected: 'FileSystem::\\LUIZMONAD\Shared\c'
    But was:  '\\LUIZMONAD\Shared\a\..\c'
               ^
   [-] must support powershell drives 14ms (4ms|10ms)
    Expected: 'HKLM:\Software\Classes\.dll'
    But was:  'Cannot find drive. A drive with the name '\HKLM' does not exist.'
               ^
   [+] works with non-existant paths 3ms (2ms|1ms)
   [-] works with non-existant drives 4ms (3ms|1ms)
    Expected: 'U:\fred\frag'
    But was:  'Cannot find drive. A drive with the name '\U' does not exist.'
               ^
   [-] barely work for direct UNCs 3ms (3ms|1ms)
    Expected: '\\LUIZMONAD\Shared\c'
    But was:  '\\LUIZMONAD\Shared\a\..\c'
               -------------------^
 Context reroot
   [+] doesn't reroot subdir 3ms (2ms|1ms)
   [+] doesn't reroot local 33ms (33ms|1ms)
   [-] doesn't reroot parent 4ms (3ms|1ms)
    Expected: 'fred\frag'
    But was:  '\fred\frag'
               ^
 Context drive root
   [+] works on drive root 5ms (3ms|2ms)
 Context temp drive
   [+] works on temp drive 4ms (3ms|1ms)
   [-] works on temp drive with absolute path 6ms (5ms|1ms)
    Expected: 'temp:\fred\frag\'
    But was:  'Cannot find drive. A drive with the name '\temp' does not exist.'
               ^
 Context unc drive
   [+] works on unc drive 6ms (5ms|1ms)
Tests completed in 207ms
Tests Passed: 9, Failed: 6, Skipped: 0 NotRun: 0

이 경우 드라이버/프로바이더/언c를 제거하고GetUnresolvedProviderPathFromPSPath드라이버/카운터/카운터를 원래대로 되돌립니다.유감스럽게도 GetUPFP는 전류에 의존합니다.pwd하지만 적어도 우리는 그걸 바꾸지 않을 거야

$path_drive = [ref] $null
$path_abs = $ExecutionContext.SessionState.Path.IsPSAbsolute($path, $path_drive)
$path_prov = $ExecutionContext.SessionState.Path.IsProviderQualified($path)
# we split the drive away, it makes UnresolvedPath fail on non-existing drives.
$norm_path  = Split-Path $path -NoQualifier
# strip out UNC
$path_direct = $norm_path.StartsWith('//') -or $norm_path.StartsWith('\\')
if ($path_direct) {
    $norm_path = $norm_path.Substring(2)
}
# then normalize
$norm_path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($norm_path)
# then we cut out the current location if same drive
if (($path_drive.Value -eq $pwd.Drive.Name) -or $path_direct) {
    $norm_path = $norm_path.Substring($pwd.Path.Trim('/', '\').Length + 1)
} elseif (-not $path_prov) {
    # or we cut out the current drive
    if ($pwd.Drive) {
        $norm_path = $norm_path.Substring($pwd.Drive.Root.Length)
    } else {
        # or we cut out the UNC special case
        $norm_path = $norm_path.Substring($pwd.ProviderPath.Length + 1)
    }
}
# then add back the UNC if any
if ($path_direct) {
    $norm_path = $pwd.Provider.ItemSeparator + $pwd.Provider.ItemSeparator + $norm_path
}
# then add back the provider if any
if ($path_prov) {
    $norm_path = $ExecutionContext.SessionState.Path.Combine($path_drive.Value + '::/', $norm_path)
}
# or add back the drive if any
elseif ($path_abs) {
    $norm_path = $ExecutionContext.SessionState.Path.Combine($path_drive.Value + ':', $norm_path)
}
$norm_path
pros: doesn't use the dotnet path function, uses proper powershell infrastructure.
cons: kind of complex, depends on `pwd`

 Context cwd
   [+] has no external libraries 8ms (2ms|6ms)
   [+] barely work for FileInfos on existing paths 4ms (3ms|1ms)
   [+] process .. and . (relative paths) 3ms (2ms|1ms)
   [+] must support powershell providers 13ms (13ms|0ms)
   [+] must support powershell drives 3ms (2ms|1ms)
   [+] works with non-existant paths 3ms (2ms|0ms)
   [+] works with non-existant drives 3ms (2ms|1ms)
   [+] barely work for direct UNCs 3ms (2ms|1ms)
 Context reroot
   [+] doesn't reroot subdir 3ms (2ms|1ms)
   [+] doesn't reroot local 3ms (2ms|1ms)
   [+] doesn't reroot parent 15ms (14ms|1ms)
 Context drive root
   [+] works on drive root 4ms (3ms|1ms)
 Context temp drive
   [+] works on temp drive 4ms (3ms|1ms)
   [+] works on temp drive with absolute path 3ms (3ms|1ms)
 Context unc drive
   [+] works on unc drive 9ms (8ms|1ms)
Tests completed in 171ms
Tests Passed: 15, Failed: 0, Skipped: 0 NotRun: 0

네, 이렇게요. 왜냐하면 그게 네가 과학자일 때 하는 일이니까.그러니까 독이 너무 복잡하면 골라도 돼
하려면 경로 스택을 .「 」의 주세요. 그렇게 하려면 경로 스택을 사용해야 합니다.GetUnresolvedProviderPathFromPSPath믿지 않으시거나 아니시다면, 정규식으로는 재귀 때문에 그렇게 할 수 없습니다.

출처 : https://gist.github.com/Luiz-Monad/d5aea290087a89c070da6eec84b33742#file-normalize-path-ps-md

만약 당신이 그것을 없애야 한다면..시스템을 사용할 수 있습니다.IO. 디렉토리Info 오브젝트'fred\frog'를 사용합니다.컨스트럭터에 \contractions'가 있습니다.FullName 속성은 정규화된 디렉토리 이름을 제공합니다.

유일한 단점은 전체 경로를 제공한다는 것입니다(예: c:\test\fred\frag).

여기에서 코멘트의 편리한 부분은 관련 경로와 절대 경로를 통합하도록 결합되었다.

[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

한 가지 방법은 다음과 같습니다.

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

잠깐만요, 제가 질문을 잘못 이해했나 봐요.이 예에서 frag는 개구리의 하위 폴더입니까?

언급URL : https://stackoverflow.com/questions/495618/how-to-normalize-a-path-in-powershell

반응형