2009-01-30 13 views
70

Tengo dos caminos:¿Cómo se normaliza una ruta en PowerShell?

fred\frog 

y

..\frag 

que puede unirse a ellos juntos en PowerShell como esto:

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

que me da esto:

fred\frog\..\frag 

Pero no quiero eso. Quiero una ruta normalizada sin los puntos dobles, como esta:

fred\frag 

¿Cómo puedo obtener eso?

+1

¿Es frag una subcarpeta de rana? Si no, combinar el camino te haría fred \ frog \ frag. Si es así, esa es una pregunta muy diferente. – EBGreen

Respuesta

59

Puede usar una combinación de pwd, Join-Path y [System.IO.Path]::GetFullPath para obtener una ruta expandida totalmente calificada.

Desde cd (Set-Location) no cambia el actual proceso de directorio de trabajo, simplemente pasando un nombre de archivo en relación con un API .NET que no entiende el contexto PowerShell, puede tener efectos secundarios no deseados, tales como la resolución de una ruta basada en el directorio de trabajo inicial (no su ubicación actual).

Lo que se hace es primera vez que cumple con los requisitos de su trayectoria:

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

Esta rendimientos (dado mi ubicación actual):

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

Con una base absoluta, es seguro que llamar al.API NET GetFullPath:

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

que le da la ruta completa y con la .. eliminado:

C:\WINDOWS\system32\fred\frag 

No es complicado, ya sea, en lo personal, Desdeño las soluciones que dependen de scripts externos de este, es un problema simple resuelto bastante bien por Join-Path y pwd (GetFullPath es solo para hacerlo bonito). Si solo desea mantener solo la parte relativa, solo agregue .Substring((pwd).Path.Trim('\').Length + 1) y ¡listo!

fred\frag 

ACTUALIZACIÓN

Gracias a @Dangph por señalar el caso C:\ borde.

+0

El último paso no funciona si pwd es "C: \". En ese caso obtengo "red \ frag". –

+0

@Dangph - No estoy seguro de entender lo que quieres decir, lo anterior parece estar funcionando bien? ¿Qué versión de PowerShell estás usando? Estoy usando la versión 3.0. –

+1

Me refiero al último paso: 'cd c: \; "C: \ fred \ frag" .Substring ((pwd) .Path.Length + 1) '. No es gran cosa; algo de lo que tener cuidado –

0

Bueno, una forma sería:

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

Espera, tal vez entienda mal la pregunta. En su ejemplo, ¿es frag una subcarpeta de rana?

+0

"¿está frag una subcarpeta de rana?" No. El .. significa subir un nivel. frag es una subcarpeta (o un archivo) en fred. –

0

Si necesita deshacerse de la parte .., puede usar un objeto System.IO.DirectoryInfo. Use 'fred \ frog .. \ frag' en el constructor. La propiedad FullName le dará el nombre del directorio normalizado.

El único inconveniente es que le dará la ruta completa (por ejemplo, c: \ test \ fred \ frag).

19

También podría usar Path.GetFullPath, aunque (como con la respuesta de Dan R) esto le dará la ruta completa. Uso sería la siguiente:

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

o más interesante

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

ambos de los cuales dió la siguiente (suponiendo que el directorio actual es D: \):

D:\fred\frag 

Tenga en cuenta que este El método no intenta determinar si realmente existe fragmentación o fragmentación.

+0

Eso se está acercando, pero cuando lo intento obtengo "H: \ fred \ frag" aunque mi directorio actual es "C: \ scratch", lo cual es incorrecto. (No debería hacer eso de acuerdo con MSDN.) Sin embargo, me dio una idea. Lo agregaré como una respuesta. –

+8

Su problema es que necesita establecer el directorio actual en .NET. '[System.IO.Directory] :: SetCurrentDirectory (((Get-Location -PSProvider FileSystem) .ProviderPath))' – JasonMArcher

+2

Solo para indicarlo explícitamente: '[IO.Path] :: GetFullPath()', a diferencia del original de PowerShell ' Resolve-Path', también funciona con rutas no existentes. Su inconveniente es la necesidad de sincronizar la carpeta de trabajo .NET con PS 'primero, como señala @JasonMArcher. – mklement0

1

Esto le da a la ruta completa:

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

Esto le da a la ruta relativa al directorio actual:

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

Por alguna razón que sólo funcionan si frag es un archivo, no un directory.

+1

gci es un alias para get-childitem. Los hijos de un directorio son sus contenidos. Reemplace gci con gi y debería funcionar para ambos. – zdan

+2

Get-Item funcionó muy bien. Pero, de nuevo, este enfoque requiere que existan las carpetas. –

92

pueden ampliar .. \ frag en todo su camino con determinación de la ruta:

PS > resolve-path ..\frag 

Trate de normalizar la ruta utilizando el método de combinar():

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

Resolve-Path solo funciona si la ruta existe. – Timbo

+0

¿Qué sucede si su ruta es 'C: \ Windows' frente a' C: \ Windows \ 'la misma ruta pero dos resultados diferentes –

+1

Los parámetros para' [io.path] :: Combine' están invertidos. Mejor aún, utilice el comando nativo 'Join-Path' PowerShell:' Join-Path (Resolver-Path ... \ frag) .Path 'fred \ frog'' También tenga en cuenta que, al menos desde PowerShell v3, 'Resolver -Path' ahora admite el modificador '-Relative' para resolver una ruta relativa a la carpeta actual. Como se mencionó, 'Resolve-Path' solo funciona con rutas existentes, a diferencia de' [IO.Path] :: GetFullPath() '. – mklement0

3

Esta biblioteca es buena : NDepend.Helpers.FileDirectoryPath.

EDIT: Esto es lo que ocurrió:

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

Llamada así:

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

Tenga en cuenta que este fragmento requiere la ruta de la DLL. Hay un truco que puedes usar para encontrar la carpeta que contiene el script que se está ejecutando actualmente, pero en mi caso tenía una variable de entorno que podía usar, así que acabo de usar eso.

+0

No sé por qué obtuvieron un voto negativo. Esa biblioteca es realmente buena para hacer manipulaciones de ruta. Es lo que terminé usando en mi proyecto. –

+0

Menos 2. Todavía desconcertado.Espero que las personas se den cuenta de que es fácil usar conjuntos .Net de PowerShell. –

+0

Esto no parece ser la mejor solución, pero es perfectamente válido. – JasonMArcher

9

Cualquier función de manipulación de rutas no PowerShell (como las de System.IO.Path) no será confiable de PowerShell porque el modelo de proveedor de PowerShell permite que la ruta actual de PowerShell difiera de lo que Windows considera que es el directorio de trabajo del proceso. Además, como ya habrá descubierto, los cmdlets Resolve-Path y Convert-Path de PowerShell son útiles para convertir rutas relativas (las que contienen '..' s) a rutas absolutas calificadas por unidad pero fallan si la ruta referenciada no existe.

El siguiente cmdlet muy simple debería funcionar para rutas no existentes. Convertirá 'fred \ frog \ .. \ frag' en 'd: \ fred \ frag' incluso si no se puede encontrar un archivo 'fred' o 'frag' o una carpeta (y la unidad actual de PowerShell es 'd:') .

function Get-AbsolutePath { 
    [CmdletBinding()] 
    param (
     [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 
     [string[]] 
     $Path 
    ) 

    process { 
     $Path | ForEach-Object { 
      $PSCmdlet.SessionState.Path.GetUnresolvedProviderPathFromPSPath($_) 
     } 
    } 
} 
+2

Esto no funciona para rutas inexistentes donde la letra de la unidad no existe, p. No tengo Q: unidad. 'Get-AbsolutePath q: \ foo \ bar \ .. \ baz' falla aunque sea una ruta válida. Bueno, dependiendo de tu definición de un camino válido. :-) FWIW, incluso el 'Test-Path -IsValid' incorporado falla en rutas enraizadas en unidades que no existen. –

2

Crear una función. Esta función normalizará una ruta que no existe en su sistema y no agregará letras de unidades.

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' 

gracias salen a Oliver Schadlich en busca de ayuda en la expresión regular.

13

La respuesta aceptada fue de gran ayuda, sin embargo, no "normaliza" correctamente una ruta absoluta también. Encuentre a continuación mi trabajo derivado que normaliza las rutas absolutas y relativas.

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

Las partes convenientes de los comentarios aquí combinan de tal manera que unifican las rutas relativas y absolutas:

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

algunos ejemplos:

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

de salida:

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 
Cuestiones relacionadas