J'ai deux chemins:
fred\frog
et
..\frag
Je peux les réunir 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 double point, 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éveloppé complet.
Étant donné que cd
(Set-Location
) Ne modifie pas le répertoire de travail actuel du processus, le simple fait de passer un nom de fichier relatif à une API .NET qui ne comprend pas le contexte PowerShell peut avoir des effets secondaires involontaires , comme la résolution d'un chemin basé sur le répertoire de travail initial (pas votre emplacement actuel).
Ce que vous faites, c'est d'abord que vous qualifiez votre chemin:
Join-Path (Join-Path (pwd) fred\frog) '..\frag'
Cela donne (compte tenu de mon emplacement actuel):
C:\WINDOWS\system32\fred\frog\..\frag
Avec une base absolue, il est sûr d'appeler l'API .NET GetFullPath
:
[System.IO.Path]::GetFullPath((Join-Path (Join-Path (pwd) fred\frog) '..\frag'))
Ce qui vous donne le chemin d'accès complet et avec le ..
Supprimé:
C:\WINDOWS\system32\fred\frag
Ce n'est pas compliqué non plus, personnellement, je dédaigne les solutions qui dépendent de scripts externes pour cela, c'est un problème simple résolu assez judicieusement par Join-Path
Et pwd
(GetFullPath
c'est juste pour faire c'est joli). Si vous voulez seulement garder seulement la partie relative, il vous suffit d'ajouter .Substring((pwd).Path.Trim('\').Length + 1)
et le tour est joué!
fred\frag
Merci à @Dangph d'avoir souligné le cas C:\
Edge.
Vous pouvez étendre ..\frag à 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 produisent les éléments suivants (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 existent réellement.
La réponse acceptée a été d'une grande aide, mais elle ne "normalise" pas correctement un chemin absolu. Retrouvez 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 strips out any relative path modifiers like '..' and '.'
$Path = [System.IO.Path]::GetFullPath($Path);
return $Path;
}
Toutes les fonctions de manipulation de chemin non PowerShell (telles que celles de System.IO.Path) ne seront pas fiables à partir de PowerShell car le modèle de fournisseur de PowerShell permet au chemin actuel de PowerShell de différer de ce que Windows pense que le répertoire de travail du processus est.
En outre, 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 (ceux contenant des "..") en chemins absolus qualifiés de lecteur, 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 inexistants. Il convertira "fred\frog\..\frag" en "d:\fred\frag" même si aucun fichier ou dossier "fred" ou "frag" ne peut être trouvé (et le lecteur PowerShell actuel est "d:") .
function Get-AbsolutePath {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[string[]]
$Path
)
process {
$Path | ForEach-Object {
$PSCmdlet.SessionState.Path.GetUnresolvedProviderPathFromPSPath($_)
}
}
}
Cette bibliothèque est bonne: NDepend.Helpers.FileDirectoryPath .
EDIT: Voici ce que j'ai trouvé:
[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
}
Appelez ça comme ceci:
> NormalizePath "fred\frog\..\frag"
fred\frag
Notez que cet extrait nécessite le chemin d'accès à 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, donc je viens de l'utiliser.
Créez une fonction. Cette fonction normalisera un chemin d'accès qui n'existe pas sur votre système et n'ajoutera pas de lettres de lecteurs.
function RemoveDotsInPath {
[cmdletbinding()]
Param( [Parameter(Position=0, Mandatory=$true)] [string] $PathString = '' )
$newPath = $PathString -creplace '(?<grp>[^\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 par rapport 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ésoudre le chemin qui pourrait ne pas exister? normalisera le chemin. Si le chemin n'inclut pas le qualificatif, il sera toujours normalisé mais renverra le chemin complet par rapport au répertoire actuel, 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, une façon serait:
Join-Path 'fred\frog' '..\frag'.Replace('..', '')
Attendez, je comprends peut-être 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 objet 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 utiles des commentaires ici combinés de telle sorte qu'ils 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