Mam dwie ścieżki:
fred\frog
i
..\frag
Mogę połączyć je razem w PowerShell w ten sposób:
join-path 'fred\frog' '..\frag'
To daje mi to:
fred\frog\..\frag
Ale ja tego nie chcę. Chcę znormalizowanej ścieżki bez podwójnych kropek, na przykład:
fred\frag
Jak mogę to uzyskać?
powershell
path
dan-gph
źródło
źródło
Odpowiedzi:
Można użyć kombinacji
pwd
,Join-Path
i[System.IO.Path]::GetFullPath
aby uzyskać pełną rozszerzoną ścieżkę.Ponieważ
cd
(Set-Location
) nie zmienia bieżącego katalogu roboczego procesu, po prostu przekazanie względnej nazwy pliku do interfejsu API .NET, które nie rozumie kontekstu programu PowerShell, może mieć niezamierzone skutki uboczne, takie jak rozpoznawanie ścieżki na podstawie początkowej pracy katalog (nie bieżąca lokalizacja).Najpierw kwalifikujesz swoją ścieżkę:
Join-Path (Join-Path (pwd) fred\frog) '..\frag'
To daje (biorąc pod uwagę moją obecną lokalizację):
Mając absolutną podstawę, można bezpiecznie wywołać interfejs .NET API
GetFullPath
:[System.IO.Path]::GetFullPath((Join-Path (Join-Path (pwd) fred\frog) '..\frag'))
Co daje w pełni kwalifikowaną ścieżkę i
..
usunięte:To też nie jest skomplikowane, osobiście gardzę rozwiązaniami, które w tym zakresie zależą od zewnętrznych skryptów, jest to prosty problem rozwiązany dość trafnie przez
Join-Path
ipwd
(GetFullPath
ma tylko uczynić go ładnym). Jeśli chcesz zachować tylko względną część , po prostu dodaj.Substring((pwd).Path.Trim('\').Length + 1)
i voila!AKTUALIZACJA
Podziękowania dla @Dangph za wskazanie
C:\
przypadku krawędzi.źródło
cd c:\; "C:\fred\frag".Substring((pwd).Path.Length + 1)
. To nic wielkiego; po prostu coś, o czym należy pamiętać.cd c:\; "C:\fred\frag".Substring((pwd).Path.Trim('\').Length + 1)
. Ale robi się trochę długo.Możesz rozwinąć .. \ frag do pełnej ścieżki za pomocą rozwiązania-path:
Spróbuj znormalizować ścieżkę za pomocą metody connect ():
[io.path]::Combine("fred\frog",(resolve-path ..\frag).path)
źródło
C:\Windows
przeciwkoC:\Windows\
tej samej ścieżce, ale dwa różne wyniki[io.path]::Combine
są odwrócone. Jeszcze lepiej, użyj natywnegoJoin-Path
polecenia programu PowerShell: należyJoin-Path (Resolve-Path ..\frag).Path 'fred\frog'
również zauważyć, że przynajmniej od wersji PowerShell v3Resolve-Path
obsługuje teraz-Relative
przełącznik do rozpoznawania ścieżki względem bieżącego folderu. Jak wspomniano,Resolve-Path
działa tylko z istniejącymi ścieżkami, w przeciwieństwie do[IO.Path]::GetFullPath()
.Możesz także użyć Path.GetFullPath , chociaż (podobnie jak w przypadku odpowiedzi Dana R.) da ci to całą ścieżkę. Sposób użycia byłby następujący:
[IO.Path]::GetFullPath( "fred\frog\..\frag" )
lub co ciekawsze
[IO.Path]::GetFullPath( (join-path "fred\frog" "..\frag") )
oba dają następujące wyniki (zakładając, że twój bieżący katalog to D: \):
Zauważ, że ta metoda nie próbuje określić, czy fred lub frag rzeczywiście istnieją.
źródło
[System.IO.Directory]::SetCurrentDirectory(((Get-Location -PSProvider FileSystem).ProviderPath))
[IO.Path]::GetFullPath()
w przeciwieństwie do natywnego programu PowerShellResolve-Path
działa również z nieistniejącymi ścieżkami. Jego wadą jest konieczność zsynchronizowania najpierw folderu roboczego .NET z PS, jak wskazuje @JasonMArcher.Join-Path
powoduje wyjątek, jeśli odnoszą się do dysku, który nie istnieje.Przyjęta odpowiedź była bardzo pomocna, ale nie „normalizuje” również właściwie ścieżki absolutnej. Znajdź poniżej moją pracę pochodną, która normalizuje zarówno ścieżki bezwzględne, jak i względne.
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; }
źródło
[IO.Path]::GetFullPath()
nie określa poprawnie katalogu dla zwykłej nazwy pliku.Wszelkie funkcje manipulacji ścieżkami inne niż PowerShell (takie jak te w System.IO.Path) nie będą niezawodne w programie PowerShell, ponieważ model dostawcy programu PowerShell pozwala, aby bieżąca ścieżka programu PowerShell różniła się od tego, co system Windows uważa za katalog roboczy procesu.
Ponadto, jak być może już odkryłeś, polecenia cmdlet programu PowerShell Resolve-Path i Convert-Path są przydatne do konwertowania ścieżek względnych (zawierających „..”) na ścieżki bezwzględne kwalifikowane przez dysk, ale kończą się niepowodzeniem, jeśli ścieżka, do której się odwołujesz, nie istnieje.
Poniższe bardzo proste polecenie cmdlet powinno działać w przypadku nieistniejących ścieżek. Konwertuje „fred \ frog \ .. \ frag” na „d: \ fred \ frag”, nawet jeśli nie można znaleźć pliku lub folderu „fred” lub „frag” (a bieżący dysk PowerShell to „d:”) .
function Get-AbsolutePath { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [string[]] $Path ) process { $Path | ForEach-Object { $PSCmdlet.SessionState.Path.GetUnresolvedProviderPathFromPSPath($_) } } }
źródło
Get-AbsolutePath q:\foo\bar\..\baz
kończy się niepowodzeniem, mimo że jest to prawidłowa ścieżka. Cóż, w zależności od twojej definicji ważnej ścieżki. :-) FWIW, nawet wbudowanyTest-Path <path> -IsValid
nie działa na ścieżkach zakorzenionych w dyskach, które nie istnieją.HKLM:\SOFTWARE
jest to prawidłowa ścieżka w programie PowerShell, odnosząca się doSOFTWARE
klucza w gałęzi rejestru komputera lokalnego. Ale aby dowiedzieć się, czy jest prawidłowy, musi dowiedzieć się, jakie są reguły dotyczące ścieżek rejestru.Ta biblioteka jest dobra: NDepend.Helpers.FileDirectoryPath .
EDYCJA: Oto, co wymyśliłem:
[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 }
Nazwij to tak:
> NormalizePath "fred\frog\..\frag" fred\frag
Należy zauważyć, że ten fragment kodu wymaga ścieżki do biblioteki DLL. Istnieje sztuczka, której możesz użyć, aby znaleźć folder zawierający aktualnie wykonywany skrypt, ale w moim przypadku miałem zmienną środowiskową, której mogłem użyć, więc po prostu jej użyłem.
źródło
To daje pełną ścieżkę:
(gci 'fred\frog\..\frag').FullName
To daje ścieżkę względną do bieżącego katalogu:
(gci 'fred\frog\..\frag').FullName.Replace((gl).Path + '\', '')
Z jakiegoś powodu działają tylko wtedy, gdy
frag
jest to plik, a niedirectory
.źródło
Utwórz funkcję. Ta funkcja znormalizuje ścieżkę, której nie ma w systemie, a także nie doda liter dysków.
function RemoveDotsInPath { [cmdletbinding()] Param( [Parameter(Position=0, Mandatory=$true)] [string] $PathString = '' ) $newPath = $PathString -creplace '(?<grp>[^\n\\]+\\)+(?<-grp>\.\.\\)+(?(grp)(?!))', '' return $newPath }
Dawny:
$a = 'fooA\obj\BusinessLayer\..\..\bin\BusinessLayer\foo.txt' RemoveDotsInPath $a 'fooA\bin\BusinessLayer\foo.txt'
Podziękowania dla Olivera Schadlicha za pomoc w RegEx.
źródło
somepaththing\.\filename.txt
jak to, że zachowuje tę pojedynczą kropkęJeśli ścieżka zawiera kwalifikator (literę dysku), to odpowiedź x0n na Powershell: rozwiązać ścieżkę, która może nie istnieć? znormalizuje ścieżkę. Jeśli ścieżka nie zawiera kwalifikatora, nadal będzie znormalizowana, ale zwróci w pełni kwalifikowaną ścieżkę względem bieżącego katalogu, co może nie być tym, czego chcesz.
$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
źródło
Jeśli chcesz pozbyć się części .., możesz użyć obiektu System.IO.DirectoryInfo. Użyj 'fred \ frog .. \ frag' w konstruktorze. Właściwość FullName poda znormalizowaną nazwę katalogu.
Jedyną wadą jest to, że poda Ci całą ścieżkę (np. C: \ test \ fred \ frag).
źródło
Celowe części komentarzy zostały połączone w taki sposób, że ujednoliciły ścieżki względne i bezwzględne:
[System.IO.Directory]::SetCurrentDirectory($pwd) [IO.Path]::GetFullPath($dapath)
Niektóre próbki:
$fps = '.', 'file.txt', '.\file.txt', '..\file.txt', 'c:\somewhere\file.txt' $fps | % { [IO.Path]::GetFullPath($_) }
wynik:
źródło
Cóż, jednym ze sposobów byłoby:
Join-Path 'fred\frog' '..\frag'.Replace('..', '')
Czekaj, może źle zrozumiałem pytanie. Czy w twoim przykładzie frag jest podfolderem żaby?
źródło