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
'bestsource' 카테고리의 다른 글
단일 파일을 이전 버전으로 되돌리려면 어떻게 해야 합니까? (0) | 2023.04.19 |
---|---|
libv8 설치 중 오류: 오류: gem 네이티브 확장을 빌드하지 못했습니다. (0) | 2023.04.19 |
CMD 쉘의 사용 가능한 공간 (0) | 2023.04.19 |
STDOUT와 STDERR를 모두 단말기와 로그파일에 보내려면 어떻게 해야 하나요? (0) | 2023.04.19 |
WPF에서의 탭 순서 설정 (0) | 2023.04.19 |