Pregunta Cómo normalizar una ruta en PowerShell?


Tengo dos caminos:

fred\frog

y

..\frag

Puedo unirlos en PowerShell de esta manera:

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

Eso me da esto:

fred\frog\..\frag

Pero no quiero eso. Quiero un camino normalizado sin los puntos dobles, como este:

fred\frag

¿Cómo puedo obtener eso?


74
2018-01-30 14:15


origen


Respuestas:


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

Ya que cd (Set-Location) no cambia el directorio de trabajo actual del proceso, simplemente transfiriendo un nombre de archivo relativo a una API .NET que no comprende el contexto de PowerShell, puede tener efectos secundarios no deseados, como resolver una ruta basada en el directorio de trabajo inicial ( no su ubicación actual).

Lo que haces es primero calificar tu camino:

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

Esto produce (dada mi ubicación actual):

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

Con una base absoluta, es seguro llamar a la API de .NET GetFullPath:

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

Lo que le da la ruta completamente calificada y con el .. remoto:

C:\WINDOWS\system32\fred\frag

No es complicado tampoco, personalmente, desdeño las soluciones que dependen de guiones externos para esto, es un problema simple resuelto bastante bien por Join-Path y pwd (GetFullPath es solo para hacerlo bonito). Si solo quieres mantener solo la parte relativa, solo agrega .Substring((pwd).Path.Trim('\').Length + 1) ¡y voilá!

fred\frag

ACTUALIZAR

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


65
2017-12-12 19:39



Puede expandir .. \ frag a su ruta completa con resolve-path:

PS > resolve-path ..\frag 

Intenta normalizar la ruta con el método combine ():

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

94
2018-01-31 16:25



También podrías usar Path.GetFullPath, aunque (como con la respuesta de Dan R) esto te dará el camino completo. El uso sería el siguiente:

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

o más interesante

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

ambos dan el siguiente (asumiendo que su directorio actual es D: \):

D:\fred\frag

Tenga en cuenta que este método no intenta determinar si realmente existen fred o frag.


22
2018-01-30 15:22



La respuesta aceptada fue de gran ayuda, sin embargo, no "normaliza" correctamente un camino absoluto 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;
}

14
2018-06-06 14:18



Cualquier función de manipulación de ruta que no sea de PowerShell (como las de System.IO.Path) no será confiable desde 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 para unidades, pero fallan si la ruta a la que se hace referencia 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($_)
        }
    }
}

10
2018-02-13 02:51



Esta biblioteca es buena: NDepend.Helpers.FileDirectoryPath.

EDITAR: Esto es lo que se me 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
}

Llámalo así:

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

Tenga en cuenta que este fragmento requiere la ruta a 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.


3
2018-02-28 05:05



Crea 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 a Oliver Schadlich por su ayuda en el RegEx.


2
2017-07-20 22:57



Esto le da la ruta completa:

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

Esto proporciona la ruta relativa al directorio actual:

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

Por alguna razón, solo funcionan si frag es un archivo, no un directory.


1
2018-01-31 01:46



Bueno, una forma sería:

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

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


0
2018-01-30 14:26



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).


0
2018-01-30 14:37