Comment normaliser un chemin dans PowerShell?

J’ai deux chemins:

fred\frog 

et

 ..\frag 

Je peux les rejoindre dans PowerShell comme ceci:

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

Cela me donne ceci:

 fred\frog\..\frag 

Mais je ne veux pas ça. Je veux un chemin normalisé sans les doubles points, comme ceci:

 fred\frag 

Comment puis-je l’obtenir?

Vous pouvez utiliser une combinaison de pwd , Join-Path et [System.IO.Path]::GetFullPath pour obtenir un chemin d’access complet qualifié.

Étant donné que cd ( Set-Location ) ne modifie pas le répertoire de travail courant du processus, il est possible que des effets secondaires non désirés, tels que la résolution d’un chemin, soient simplement transmis à une API .NET qui ne comprend pas le contexte PowerShell. hors du répertoire de travail initial (pas votre emplacement actuel).

Qu’est-ce que vous faites est que vous qualifiez d’abord votre chemin:

 Join-Path (Join-Path (pwd) fred\frog) '..\frag' 

Cela donne (vu mon emplacement actuel):

 C:\WINDOWS\system32\fred\frog\..\frag 

Avec une base absolue, vous pouvez appeler l’API GetFullPath API .NET:

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

Ce qui vous donne le chemin complet et avec le .. supprimé:

 C:\WINDOWS\system32\fred\frag 

Ce n’est pas compliqué non plus, personnellement, je dédaignais les solutions qui dépendent de scripts externes pour cela, c’est un problème simple résolu assez bien par Join-Path et pwd ( GetFullPath est juste pour le rendre joli). Si vous voulez seulement ne garder que la partie relative , ajoutez simplement .Subssortingng((pwd).Path.Trim('\').Length + 1) et le tour est joué!

 fred\frag 

METTRE À JOUR

Merci à @Dangph pour avoir souligné le cas C:\ edge.

Vous pouvez développer .. \ frag vers son chemin complet avec resolve-path:

 PS > resolve-path ..\frag 

Essayez de normaliser le chemin en utilisant la méthode combine ():

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

Vous pouvez également utiliser Path.GetFullPath , bien que (comme avec la réponse de Dan R) cela vous donnera le chemin complet. L’utilisation serait la suivante:

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

ou plus intéressant

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

les deux donnent le suivant (en supposant que votre répertoire actuel est D: \):

 D:\fred\frag 

Notez que cette méthode ne tente pas de déterminer si fred ou frag existe réellement.

La réponse acceptée a été d’une grande aide, mais elle ne “normalise” pas correctement un chemin absolu. Trouvez ci-dessous mon travail dérivé qui normalise les chemins absolus et relatifs.

 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 ssortingps out any relative path modifiers like '..' and '.' $Path = [System.IO.Path]::GetFullPath($Path); return $Path; } 

Toutes les fonctions de manipulation de chemin autres que PowerShell (telles que celles de System.IO.Path) ne seront pas fiables à partir de PowerShell, car le modèle de fournisseur PowerShell permet au chemin courant de PowerShell de différer de Windows.

De plus, comme vous l’avez peut-être déjà découvert, les applets de commande Resolve-Path et Convert-Path de PowerShell sont utiles pour convertir des chemins relatifs (contenant des ..) en chemins absolus qualifiés de lecteurs, mais ils échouent si le chemin référencé n’existe pas.

L’applet de commande très simple suivante devrait fonctionner pour les chemins non existants. Il convertira ‘fred \ frog \ .. \ frag’ en ‘d: \ fred \ frag’ même si un fichier ou un dossier ‘fred’ ou ‘frag’ est introuvable (et que le lecteur PowerShell actuel est ‘d:’) .

 function Get-AbsolutePath { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [ssortingng[]] $Path ) process { $Path | ForEach-Object { $PSCmdlet.SessionState.Path.GetUnresolvedProviderPathFromPSPath($_) } } } 

Cette bibliothèque est bien: NDepend.Helpers.FileDirectoryPath .

EDIT: C’est ce que j’ai imaginé:

 [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.SubSsortingng(2) } $result } 

Appelez-le comme ceci:

 > NormalizePath "fred\frog\..\frag" fred\frag 

Notez que cet extrait requirejs le chemin d’access à la DLL. Il y a une astuce que vous pouvez utiliser pour trouver le dossier contenant le script en cours d’exécution, mais dans mon cas, j’avais une variable d’environnement que je pouvais utiliser.

Créer une fonction Cette fonction va normaliser un chemin qui n’existe pas sur votre système et ne pas append de lettres de lecteurs.

 function RemoveDotsInPath { [cmdletbinding()] Param( [Parameter(Position=0, Mandatory=$true)] [ssortingng] $PathSsortingng = '' ) $newPath = $PathSsortingng -creplace '(?[^\n\\]+\\)+(?< -grp>\.\.\\)+(?(grp)(?!))', '' return $newPath } 

Ex:

 $a = 'fooA\obj\BusinessLayer\..\..\bin\BusinessLayer\foo.txt' RemoveDotsInPath $a 'fooA\bin\BusinessLayer\foo.txt' 

Merci à Oliver Schadlich pour son aide dans le RegEx.

Cela donne le chemin complet:

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

Cela donne le chemin relatif au répertoire courant:

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

Pour une raison quelconque, ils ne fonctionnent que si frag est un fichier, pas un directory .

Si le chemin inclut un qualificatif (lettre de lecteur) alors la réponse de x0n à Powershell: résout le chemin qui pourrait ne pas exister? va normaliser le chemin. Si le chemin n’inclut pas le qualificatif, il sera toujours normalisé mais renverra le chemin qualifié complet relatif au répertoire en cours, ce qui peut ne pas être ce que vous voulez.

 $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 

Eh bien, un moyen serait:

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

Attends, peut-être que je comprends mal la question. Dans votre exemple, frag est-il un sous-dossier de grenouille?

Si vous devez vous débarrasser de la partie .., vous pouvez utiliser un object System.IO.DirectoryInfo. Utilisez ‘fred \ frog .. \ frag’ dans le constructeur. La propriété FullName vous donnera le nom de répertoire normalisé.

Le seul inconvénient est qu’il vous donnera le chemin complet (par exemple, c: \ test \ fred \ frag).

Les parties opportunes des commentaires combinées de telle sorte qu’elles unifient les chemins relatifs et absolus:

 [System.IO.Directory]::SetCurrentDirectory($pwd) [IO.Path]::GetFullPath($dapath) 

Certains échantillons:

 $fps = '.', 'file.txt', '.\file.txt', '..\file.txt', 'c:\somewhere\file.txt' $fps | % { [IO.Path]::GetFullPath($_) } 

sortie:

 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