ejemplos - script en powershell
Cómo normalizar una ruta en PowerShell? (11)
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?
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?
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.
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($_)
}
}
}
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.
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
.
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;
}
Las partes útiles de los comentarios aquí combinados de manera que unifican caminos relativos y absolutos:
[System.IO.Directory]::SetCurrentDirectory($pwd)
[IO.Path]::GetFullPath($dapath)
Algunas muestras:
$fps = ''.'', ''file.txt'', ''./file.txt'', ''../file.txt'', ''c:/somewhere/file.txt''
$fps | % { [IO.Path]::GetFullPath($_) }
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
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)
Puede utilizar una combinación de pwd
, Join-Path
y [System.IO.Path]::GetFullPath
para obtener una ruta expandida totalmente calificada.
Dado que cd
( Set-Location
) no cambia el directorio de trabajo actual del proceso, simplemente transfiriendo un nombre de archivo relativo a una API de .NET que no comprende el contexto de PowerShell, puede tener efectos secundarios no deseados, como la resolución a una ruta basada fuera del 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 completa y con la ..
eliminada:
C:/WINDOWS/system32/fred/frag
No es complicado tampoco, personalmente, desdeño las soluciones que dependen de scripts externos para esto, es un problema simple resuelto bastante bien por Join-Path
y pwd
( GetFullPath
es solo para hacerlo bonito). Si solo quieres conservar solo la parte relativa , simplemente agrega .Substring((pwd).Path.Trim(''/').Length + 1)
y listo!
fred/frag
ACTUALIZAR
Gracias a @Dangph por señalar el caso C:/
edge.
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).
También podría usar Path.GetFullPath , aunque (como con la respuesta de Dan R) esto le dará la ruta completa. 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.