Как нормализовать путь в PowerShell? - PullRequest
78 голосов
/ 30 января 2009

У меня есть два пути:

fred\frog

и

..\frag

Я могу объединить их вместе в PowerShell следующим образом:

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

Это дает мне это:

fred\frog\..\frag

Но я этого не хочу. Я хочу нормализованный путь без двойных точек, например:

fred\frag

Как я могу получить это?

Ответы [ 12 ]

96 голосов
/ 31 января 2009

Вы можете развернуть .. \ frag до полного пути с помощью resol-path:

PS > resolve-path ..\frag 

Попробуйте нормализовать путь, используя метод comb ():

[io.path]::Combine("fred\frog",(resolve-path ..\frag).path)
72 голосов
/ 12 декабря 2012

Вы можете использовать комбинацию pwd, Join-Path и [System.IO.Path]::GetFullPath, чтобы получить полностью определенный расширенный путь.

Поскольку cd (Set-Location) не изменяет текущий рабочий каталог процесса, просто передача относительного имени файла в .NET API, который не понимает контекст PowerShell, может иметь непредвиденные побочные эффекты, такие как преобразование в путь, основанный на исходном рабочем каталоге (а не на вашем текущем местоположении).

Что вы делаете, так это сначала уточняете свой путь:

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

Это дает (учитывая мое текущее местоположение):

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

С абсолютной базой безопасно вызывать .NET API GetFullPath:

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

, который дает вам полный путь и с удаленным ..:

C:\WINDOWS\system32\fred\frag

Это тоже не сложно, лично я презираю решения, которые для этого зависят от внешних сценариев, это простая задача, довольно удачно решаемая с помощью Join-Path и pwd (GetFullPath просто для того, чтобы сделать ее красивой). Если вы хотите оставить только только относительную часть , просто добавьте .Substring((pwd).Path.Trim('\').Length + 1) и вуаля!

fred\frag

UPDATE

Спасибо @Dangph за указание на край C:\.

23 голосов
/ 30 января 2009

Вы также можете использовать Path.GetFullPath , хотя (как и в ответе Дана Р.) это даст вам весь путь. Использование будет следующим:

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

или более интересно

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

оба из которых приводят к следующему (при условии, что ваш текущий каталог - D: \):

D:\fred\frag

Обратите внимание, что этот метод не пытается определить, существуют ли на самом деле fred или frag.

17 голосов
/ 06 июня 2013

Принятый ответ очень помог, однако он также не может «нормализовать» абсолютный путь. Найдите ниже мою производную работу, которая нормализует как абсолютные, так и относительные пути.

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;
}
10 голосов
/ 13 февраля 2011

Любые функции управления путями, отличные от PowerShell (например, функции System.IO.Path), не будут надежными с PowerShell, поскольку модель поставщика PowerShell позволяет текущему пути PowerShell отличаться от того, что Windows считает рабочим каталогом процесса.

Кроме того, как вы, возможно, уже обнаружили, командлеты PowerShell Resolve-Path и Convert-Path полезны для преобразования относительных путей (содержащих «..») в квалифицированные для диска абсолютные пути, но они терпят неудачу, если путь, на который указывает ссылка, делает не существует.

Следующий очень простой командлет должен работать для несуществующих путей. Он преобразует 'fred \ frog \ .. \ frag' в 'd: \ fred \ frag', даже если не найден файл или папка 'fred' или 'frag' (а текущий диск PowerShell - 'd:') .

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

    process {
        $Path | ForEach-Object {
            $PSCmdlet.SessionState.Path.GetUnresolvedProviderPathFromPSPath($_)
        }
    }
}
3 голосов
/ 28 февраля 2009

Эта библиотека хороша: NDepend.Helpers.FileDirectoryPath .

РЕДАКТИРОВАТЬ: Это то, что я придумал:

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

Назовите это так:

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

Обратите внимание, что этот фрагмент требует путь к DLL. Есть хитрость, которую вы можете использовать, чтобы найти папку, содержащую текущий исполняемый скрипт, но в моем случае у меня была переменная окружения, которую я мог использовать, поэтому я просто использовал ее.

2 голосов
/ 21 июля 2012

Создать функцию. Эта функция нормализует путь, который не существует в вашей системе, а также не добавляет буквы дисков.

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'

Спасибо Оливеру Шадличу за помощь в RegEx.

1 голос
/ 04 сентября 2018

Если путь включает в себя квалификатор (букву диска), то x0n ответ на Powershell: разрешить путь, который может не существовать? нормализует путь. Если путь не содержит спецификатор, он все равно будет нормализован, но вернет полный путь относительно текущего каталога, что может быть не тем, что вы хотите.

$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
1 голос
/ 31 января 2009

Это дает полный путь:

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

Это дает путь относительно текущего каталога:

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

По какой-то причине они работают, только если frag является файлом, а не directory.

0 голосов
/ 23 февраля 2018

Полезные части комментариев здесь объединены так, что они объединяют относительные и абсолютные пути:

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

Некоторые образцы:

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

Выход:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...