PowerShell'de bir yol nasıl normalleştirilir?


96

İki yolum var:

fred\frog

ve

..\frag

Onları PowerShell'de şu şekilde birleştirebilirim:

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

Bu bana şunu veriyor:

fred\frog\..\frag

Ama bunu istemiyorum. Çift nokta içermeyen normalleştirilmiş bir yol istiyorum, şöyle:

fred\frag

Bunu nasıl elde edebilirim?


1
Frag, kurbağanın bir alt klasörü mü? Aksi takdirde yolu birleştirirseniz fred \ frog \ frag elde edersiniz. Öyleyse, bu çok farklı bir sorudur.
EBGreen

Yanıtlar:


83

Sen bir arada kullanabilir pwd, Join-Pathve [System.IO.Path]::GetFullPathbir tam genişletilmiş yol alır.

Yana cd( Set-Location) basitçe PowerShell bağlamda anlamak olmayan bir .NET API göreceli bir dosya adı geçen süreç Geçerli çalışma dizini değişmez böyle başlangıç çalışma dışı esaslı bir yola çözülmesi gibi istenmeyen yan etkileri olabilir dizin (mevcut konumunuz değil).

Yaptığınız şey, önce yolunuzu nitelemek:

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

Bu, (mevcut konumum göz önüne alındığında):

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

Mutlak bir temel ile, .NET API'yi çağırmak güvenlidir GetFullPath:

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

Bu size tam nitelikli yolu ve ..kaldırılmış yolu verir :

C:\WINDOWS\system32\fred\frag

Kişisel olarak da karmaşık değil, bunun için harici komut dosyalarına bağlı çözümleri küçümsüyorum, basit bir problem tarafından oldukça uygun bir şekilde çözüldü Join-Pathve pwd( GetFullPathsadece güzelleştirmek için). Sadece göreceli kısmı korumak istiyorsanız , sadece ekleyin .Substring((pwd).Path.Trim('\').Length + 1)ve tamamlayın!

fred\frag

GÜNCELLEME

En C:\uç durumu işaret ettiği için @Dangph'a teşekkürler .


Pwd "C: \" ise son adım çalışmaz. Bu durumda "kırmızı \ frag" elde ederim.
dan-gph

@Dangph - Ne demek istediğini anladığımdan emin değilim, yukarıdakiler gayet iyi çalışıyor gibi görünüyor? PowerShell'in hangi sürümünü kullanıyorsunuz? 3.0 sürümünü kullanıyorum.
John Leidegren

1
Geçen adım anlamına: cd c:\; "C:\fred\frag".Substring((pwd).Path.Length + 1). Çok önemli değil; Dikkat etmemiz gereken birşey.
dan-gph

Ah, iyi yakaladım, bir düzeltme çağrısı ekleyerek bunu düzeltebiliriz. Deneyin cd c:\; "C:\fred\frag".Substring((pwd).Path.Trim('\').Length + 1). Yine de uzun oluyor.
John Leidegren

2
Veya sadece şunu kullanın: $ ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath (". \ Nonexist \ foo.txt") Var olmayan yollarla da çalışır. "x0n" bu btw için övgüyü hak ediyor. Kendisinin de belirttiği gibi, bu, flilesystem yollarına değil, PSPaths'e çözümleniyor, ancak PowerShell'deki yolları kullanıyorsanız, kimin umurunda? stackoverflow.com/questions/3038337/…
Joe the Coder

106

.. \ frag'ı çözüm yolu ile tam yoluna genişletebilirsiniz:

PS > resolve-path ..\frag 

Comb () yöntemini kullanarak yolu normalleştirmeyi deneyin:

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

Ya bir yol olduğunda C:\Windowsvs C:\Windows\ aynı yolu ancak iki farklı sonuçlar
Joe Phillips

2
İçin parametreler [io.path]::Combinetersine çevrilmiştir. Daha da iyisi, yerel Join-PathPowerShell komutunu kullanın: Join-Path (Resolve-Path ..\frag).Path 'fred\frog'Ayrıca, en azından PowerShell v3'te Resolve-Pathartık -Relativegeçerli klasöre göre bir yola çözümleme için anahtarı desteklediğini unutmayın . Belirtildiği gibi Resolve-Path, aksine , yalnızca mevcut yollarla çalışır [IO.Path]::GetFullPath().
mklement0

25

Path.GetFullPath'i de kullanabilirsiniz , ancak (Dan R'nin cevabında olduğu gibi) bu size tüm yolu verecektir. Kullanım aşağıdaki gibi olacaktır:

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

veya daha ilginci

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

her ikisi de aşağıdakileri verir (geçerli dizininizin D: \ olduğunu varsayarsak):

D:\fred\frag

Bu yöntemin fred veya frag'ın gerçekten var olup olmadığını belirlemeye çalışmadığını unutmayın.


Bu yaklaşıyor, ancak denediğimde mevcut dizinim "C: \ scratch" olsa da "H: \ fred \ frag" alıyorum, bu yanlış. (MSDN'ye göre bunu yapmamalıdır.) Yine de bana bir fikir verdi. Bunu bir cevap olarak ekleyeceğim.
dan-gph

9
Sizin sorununuz, geçerli dizini .NET'te ayarlamanız gerekmesidir. [System.IO.Directory]::SetCurrentDirectory(((Get-Location -PSProvider FileSystem).ProviderPath))
JasonMArcher

2
Açıkça belirtmek gerekirse: [IO.Path]::GetFullPath()PowerShell'in yerelinden farklı olarak Resolve-Path, var olmayan yollarla da çalışır. @ JasonMArcher'in de belirttiği gibi, olumsuz tarafı .NET'in çalışma klasörünü önce PS 'ile senkronize etme ihtiyacıdır.
mklement0

Join-Pathvar olmayan bir sürücüye atıfta bulunulduğunda bir istisnaya neden olur.
Tahir Hassan

20

Kabul edilen cevap çok yardımcı oldu, ancak mutlak bir yolu da gerektiği gibi 'normalleştirmiyor'. Hem mutlak hem de göreceli yolları normalleştiren türev çalışmamın altında bulun.

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

Tüm farklı çözümler göz önüne alındığında, bu, tüm farklı yol türleri için işe yarar. Örnek olarak [IO.Path]::GetFullPath(), düz bir dosya adı için dizini doğru şekilde belirlemez.
Jari Turkia

10

PowerShell dışındaki yol işleme işlevleri (System.IO.Path gibi), PowerShell'den güvenilir olmayacaktır çünkü PowerShell'in sağlayıcı modeli, PowerShell'in geçerli yolunun Windows'un işlemin çalışma dizini olarak düşündüğünden farklı olmasına izin verir.

Ayrıca, zaten keşfetmiş olabileceğiniz gibi, PowerShell'in Çözüm Yolu ve Yolu Dönüştür cmdlet'leri göreceli yolları (".." içerenler) sürücü nitelikli mutlak yollara dönüştürmek için kullanışlıdır, ancak başvurulan yol yoksa başarısız olurlar.

Aşağıdaki çok basit cmdlet, var olmayan yollar için çalışmalıdır. Bir 'fred' veya 'frag' dosya veya klasör bulunamasa bile (ve mevcut PowerShell sürücüsü 'd:') 'fred \ frog \ .. \ frag'ı' d: \ fred \ frag 'olarak dönüştürecektir. .

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

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

2
Bu, sürücü harfinin bulunmadığı var olmayan yollarda çalışmaz, örneğin Q: sürücüm yok. Get-AbsolutePath q:\foo\bar\..\bazgeçerli bir yol olmasına rağmen başarısız olur. Geçerli bir yol tanımınıza bağlı olarak. :-) FWIW, yerleşik Test-Path <path> -IsValidsürücüler bile var olmayan sürücülerde köklenen yollarda başarısız oluyor.
Keith Hill

2
@KeithHill Başka bir deyişle, PowerShell var olmayan bir kök üzerindeki bir yolu geçersiz kabul eder. PowerShell, onunla çalışırken ne tür bir sağlayıcı kullanacağına karar vermek için kökü kullandığından, bunun oldukça makul olduğunu düşünüyorum. Örneğin, Yerel Makine kayıt defteri kovanındaki anahtara HKLM:\SOFTWAREbaşvuran PowerShell'de geçerli bir yoldur SOFTWARE. Ancak bunun geçerli olup olmadığını anlamak için, kayıt yolları için kuralların ne olduğunu bulması gerekir.
jpmc26

3

Bu kitaplık iyidir: NDepend.Helpers.FileDirectoryPath .

DÜZENLEME: Bulduğum şey bu:

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

Şöyle diyelim:

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

Bu kod parçacığının DLL yolunu gerektirdiğini unutmayın. Şu anda çalıştırılan betiği içeren klasörü bulmak için kullanabileceğiniz bir numara var, ancak benim durumumda kullanabileceğim bir ortam değişkenine sahiptim, bu yüzden onu kullandım.


Bunun neden olumsuz oy aldığını bilmiyorum. Bu kütüphane, yol manipülasyonları yapmak için gerçekten iyidir. Projemde kullanmaya son verdiğim şey bu.
dan-gph

Eksi 2. Hala şaşkın. Umarım insanlar PowerShell'den .Net derlemelerini kullanmanın kolay olduğunu anlarlar.
dan-gph

Bu en iyi çözüm gibi görünmüyor, ancak tamamen geçerli.
JasonMArcher

@ Jason, detayları hatırlamıyorum ama o zamanlar en iyi çözüm buydu çünkü benim özel problemimi çözen tek çözüm oydu. Ancak o zamandan beri daha iyi bir çözümün ortaya çıkması mümkündür.
dan-gph

2
3. taraf DLL'ye sahip olmak bu çözümün büyük bir dezavantajı
Louis Kottmann

1

Bu tam yolu verir:

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

Bu, geçerli dizine göre yolu verir:

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

Bazı nedenlerden dolayı, yalnızca bir dosyaysa çalışırlar frag, bir directory.


1
gci, get-childitem için bir takma addır. Bir dizinin alt öğeleri içeriğidir. Gci'yi gi ile değiştirin ve her ikisi için de çalışmalıdır.
zdan

2
Get-Item güzel çalıştı. Ancak yine, bu yaklaşım klasörlerin var olmasını gerektirir.
Peter Lillevold

1

Bir işlev oluşturun. Bu işlev, sisteminizde olmayan bir yolu normalleştirecek ve sürücü harfleri eklemeyecektir.

function RemoveDotsInPath {
  [cmdletbinding()]
  Param( [Parameter(Position=0,  Mandatory=$true)] [string] $PathString = '' )

  $newPath = $PathString -creplace '(?<grp>[^\n\\]+\\)+(?<-grp>\.\.\\)+(?(grp)(?!))', ''
  return $newPath
}

Ör:

$a = 'fooA\obj\BusinessLayer\..\..\bin\BusinessLayer\foo.txt'
RemoveDotsInPath $a
'fooA\bin\BusinessLayer\foo.txt'

Normal İfadede yardım için Oliver Schadlich'e teşekkürler.


somepaththing\.\filename.txtBunun, tek noktayı koruduğu gibi yollar için çalışmadığını unutmayın
Mark Schultheiss

1

Yol bir niteleyici (sürücü harfi) içeriyorsa, x0n'nin Powershell'e cevabı: var olmayan yolu çöz? yolu normalleştirecek. Yol niteleyiciyi içermiyorsa, yine de normalleştirilecektir, ancak istediğiniz dizine göre tam olarak nitelenmiş yolu döndürecektir, ki bu istediğinizi olmayabilir.

$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

0

.. kısmından kurtulmanız gerekirse, bir System.IO.DirectoryInfo nesnesi kullanabilirsiniz. Yapıcıda 'fred \ frog .. \ frag' kullanın. FullName özelliği size normalleştirilmiş dizin adını verecektir.

Tek dezavantajı, size tüm yolu vermesidir (örneğin c: \ test \ fred \ frag).


0

Buradaki yorumların uygun kısımları, göreceli ve mutlak yolları birleştirecek şekilde birleştirildi:

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

Bazı örnekler:

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

çıktı:

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

-1

Bir yol şu olabilir:

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

Bekle, belki soruyu yanlış anladım. Örneğinizde frag, kurbağanın bir alt klasörü mü?


"frag, kurbağanın bir alt klasörü mü?" Hayır. .., bir seviye yukarı çıkmak anlamına gelir. frag, fred'de bir alt klasördür (veya bir dosyadır).
dan-gph
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.