PowerShell में पथ को सामान्य कैसे करें?


94

मेरे पास दो रास्ते हैं:

fred\frog

तथा

..\frag

मैं उन्हें इस तरह से PowerShell में एक साथ शामिल कर सकता हूं:

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

यह मुझे देता है:

fred\frog\..\frag

लेकिन मैं ऐसा नहीं चाहता। मुझे डबल डॉट्स के बिना एक सामान्यीकृत रास्ता चाहिए, जैसे:

fred\frag

मुझे वह कैसे मिल सकता है?


1
क्या खुशबू मेंढक का एक सबफ़ोल्डर है? यदि नहीं तो पथ के संयोजन से आपको fred \ frog \ frag मिलेगा। अगर ऐसा है तो यह एक बहुत अलग सवाल है।
ईबीग्रीन

जवाबों:


81

आप एक संयोजन का उपयोग कर सकते हैं pwd, Join-Pathऔर [System.IO.Path]::GetFullPathपूरी तरह से योग्य विस्तारित पथ प्राप्त करने के लिए।

चूंकि cd( Set-Location) प्रक्रिया वर्तमान कार्य निर्देशिका को परिवर्तित नहीं करता है, बस किसी .NET फ़ाइल के सापेक्ष फ़ाइल नाम को पारित करना, जो 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और बस इसे सुंदर बनाने के लिए है)। यदि आप केवल सापेक्ष भाग ही रखना चाहते हैं , तो आप केवल जोड़ और वॉयला चाहते हैं!pwdGetFullPath.Substring((pwd).Path.Trim('\').Length + 1)

fred\frag

अपडेट करें

C:\किनारे के मामले को इंगित करने के लिए @ डांगफ के लिए धन्यवाद ।


यदि pwd "C: \" है तो अंतिम चरण काम नहीं करता है। उस स्थिति में मुझे "लाल \" सुगंध मिलती है।
दान-गफ

@ डांगफ - मुझे यकीन नहीं है कि आपका क्या मतलब है, उपरोक्त ठीक काम कर रहा है? PowerShell का कौन सा संस्करण उपयोग कर रहे हैं? मैं संस्करण 3.0 का उपयोग कर रहा हूं।
जॉन लीडग्रीन

1
मेरा मतलब है अंतिम चरण cd c:\; "C:\fred\frag".Substring((pwd).Path.Length + 1):। यह एक बड़ा सौदा नहीं है; बस ऐसा कुछ जिसके बारे में अवगत होना चाहिए।
दान-गफ

आह, अच्छी पकड़, हम एक ट्रिम कॉल जोड़कर ठीक कर सकते हैं। कोशिश करो cd c:\; "C:\fred\frag".Substring((pwd).Path.Trim('\').Length + 1)। हालांकि यह थोड़े लंबे समय के लिए हो रहा है।
जॉन लेइडरग्रेन

2
या, बस उपयोग करें: $ ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath ("। \ Nonexist \ foo.txt") गैर-मौजूद डेटा के साथ भी काम करता है। "x0n" इस btw के लिए श्रेय का हकदार है। जैसा कि वह नोट करता है, यह PSPaths को हल करता है, न कि फ़्लाइलसिस्टम पथ, लेकिन यदि आप पावरस्ले में पथों का उपयोग कर रहे हैं, तो कौन परवाह करता है? stackoverflow.com/questions/3038337/…
जो कोडर

105

आप विस्तृत कर सकते हैं .. संकल्प-पथ के साथ इसके पूर्ण पथ में \ n सुगंध:

PS > resolve-path ..\frag 

संयोजन () विधि का उपयोग करके पथ को सामान्य करने का प्रयास करें:

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

क्या होगा यदि आपका रास्ता एक ही मार्ग C:\Windowsबनाम C:\Windows\ दो अलग-अलग परिणामों पर है
जो फिलिप्स

2
के लिए पैरामीटर [io.path]::Combineउलट हैं। बेहतर अभी तक, देशी Join-Pathपॉवरशेल कमांड का उपयोग करें : Join-Path (Resolve-Path ..\frag).Path 'fred\frog'यह भी ध्यान दें कि, कम से कम पॉवरशेल v3 के रूप में, Resolve-Pathअब -Relativeवर्तमान फ़ोल्डर के सापेक्ष पथ के समाधान के लिए स्विच का समर्थन करता है । जैसा कि उल्लेख किया गया है, Resolve-Pathकेवल मौजूदा रास्तों के विपरीत काम करता है [IO.Path]::GetFullPath()
mklement0

25

आप Path.GetFullPath का उपयोग भी कर सकते हैं , हालाँकि (डैन आर के जवाब के साथ) यह आपको पूरा रास्ता देगा। उपयोग निम्नानुसार होगा:

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

या अधिक दिलचस्प है

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

दोनों जिनमें से निम्नलिखित उपज है (आपकी वर्तमान निर्देशिका डी है: \):

D:\fred\frag

ध्यान दें कि यह विधि यह निर्धारित करने का प्रयास नहीं करती है कि वास्तव में फ्रेड या नाजुक मौजूद हैं या नहीं।


यह बंद हो रहा है, लेकिन जब मैं कोशिश करता हूं तो मुझे "H: \ fred \ frag" मिलता है, भले ही मेरी वर्तमान निर्देशिका "C: \ scratch" हो, जो गलत है। (यह MSDN के अनुसार ऐसा नहीं करना चाहिए।) इसने मुझे एक विचार दिया। मैं इसे उत्तर के रूप में जोड़ूंगा।
डैन-gph

8
आपकी समस्या यह है कि आपको वर्तमान निर्देशिका को .NET में सेट करने की आवश्यकता है। [System.IO.Directory]::SetCurrentDirectory(((Get-Location -PSProvider FileSystem).ProviderPath))
जेसनम्रेचर

2
इसे स्पष्ट रूप से बताने के लिए: [IO.Path]::GetFullPath()पावरशेल के मूल के विपरीत Resolve-Path, गैर-मौजूद पथ के साथ भी काम करता है। इसका नकारात्मक पहलू यह है कि पीएस के साथ .NET के काम करने वाले फोल्डर को सबसे पहले सिंक करने की जरूरत है, क्योंकि @JasonMArcher बताते हैं।
mklement0

Join-Pathएक अपवाद का कारण बनता है अगर एक ड्राइव का उल्लेख है जो मौजूद नहीं है।
ताहिर हसन

20

स्वीकृत उत्तर एक बड़ी मदद थी, लेकिन यह एक पूर्ण मार्ग को भी ठीक से 'सामान्य' नहीं करता है। मेरे व्युत्पन्न कार्य के नीचे खोजें जो पूर्ण और सापेक्ष दोनों रास्तों को सामान्य करता है।

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

सभी अलग-अलग समाधानों को देखते हुए, यह सभी विभिन्न प्रकार के रास्तों के लिए काम करता है। एक उदाहरण के रूप में [IO.Path]::GetFullPath()एक सादे फ़ाइल नाम के लिए निर्देशिका को सही ढंग से निर्धारित नहीं किया गया है।
जरी तुर्किया

10

कोई भी गैर-पॉवरशेल पथ हेरफेर फ़ंक्शन (जैसे कि System.IO.Path में) पॉवरशेल से विश्वसनीय नहीं होगा क्योंकि पॉवरशेल का प्रदाता मॉडल पॉवरशेल के वर्तमान पथ को अलग-अलग करने की अनुमति देता है, जो कि विंडोज की प्रक्रिया की कार्यशील निर्देशिका से अलग है।

इसके अलावा, जैसा कि आप पहले से ही पता लगा चुके हैं, पॉवरशेल के रिज़ॉल्यूशन-पाथ और कन्वर्ट-पाथ cmdlets रिश्तेदार पथ (उन लोगों के लिए ..) के लिए उपयोगी हैं जो ड्राइव-योग्य निरपेक्ष पथों के लिए हैं, लेकिन वे विफल हो जाते हैं यदि संदर्भित पथ मौजूद नहीं है।

निम्नलिखित बहुत ही सरल cmdlet गैर-मौजूद पथ के लिए काम करना चाहिए। यह 'fred \ frog \ .. \' को '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($_)
        }
    }
}

2
यह बिना अस्तित्व वाले रास्तों के लिए काम नहीं करता है, जहां ड्राइव अक्षर मौजूद नहीं है। उदाहरण के लिए मेरे पास कोई Q: ड्राइव नहीं है। Get-AbsolutePath q:\foo\bar\..\bazभले ही यह एक वैध मार्ग है। खैर, एक मान्य पथ के अपने निश्चितता पर निर्भर करता है। :-) एफडब्ल्यूआईडब्ल्यू, यहां तक ​​कि अंतर्निहित Test-Path <path> -IsValidड्राइव में निहित रास्तों पर विफल रहता है जो मौजूद नहीं है।
कीथ हिल

2
@KeithHill दूसरे शब्दों में, PowerShell गैर-मौजूद रूट पर एक पथ को अमान्य मानता है। मुझे लगता है कि यह काफी उचित है क्योंकि PowerShell रूट का उपयोग यह तय करने के लिए करता है कि इसके साथ काम करते समय किस तरह के प्रदाता का उपयोग किया जाए। जैसे, स्थानीय मशीन रजिस्ट्री हाइव में कुंजी का HKLM:\SOFTWAREउल्लेख करते हुए, PowerShell में एक मान्य पथ है SOFTWARE। लेकिन यह पता लगाने के लिए कि क्या यह वैध है, यह पता लगाने की आवश्यकता है कि रजिस्ट्री पथ के नियम क्या हैं।
jpmc26

3

यह लाइब्रेरी अच्छी है: ND निर्भर.हेल्पर्स.फाइलडायरेक्ट्रीपैथ

संपादित करें: यह वही है जो मैं लेकर आया हूं:

[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 के लिए पथ की आवश्यकता होती है। एक चाल है जिसे आप वर्तमान में निष्पादित स्क्रिप्ट वाले फ़ोल्डर को खोजने के लिए उपयोग कर सकते हैं, लेकिन मेरे मामले में मेरे पास एक पर्यावरण चर था जिसका मैं उपयोग कर सकता था, इसलिए मैंने बस इसका उपयोग किया।


मैं नहीं जानता कि क्यों एक downvote मिला है। पथ हेरफेर करने के लिए वह पुस्तकालय वास्तव में अच्छा है। यह वही है जो मैंने अपने प्रोजेक्ट में इस्तेमाल किया है।
डैन-जीएफ

माइनस 2. फिर भी हैरान। मुझे आशा है कि लोगों को पता है कि इसका उपयोग करना आसान है।
डैन-जीएफ

यह सबसे अच्छा समाधान नहीं लगता है, लेकिन यह पूरी तरह से मान्य है।
जेसनम्रेचर

@ जेसन, मुझे विवरण याद नहीं है, लेकिन उस समय यह सबसे अच्छा समाधान था क्योंकि यह एकमात्र ऐसा था जिसने मेरी विशेष समस्या को हल किया। यह संभव है कि तब से एक और, बेहतर समाधान के साथ आया है।
dan-gph

2
3rd पार्टी DLL का इस समाधान के लिए एक बड़ी खामी है
लुई कोट्टमन

1

इससे पूरा रास्ता मिलता है:

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

यह वर्तमान निर्देशिका के सापेक्ष पथ देता है:

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

किसी कारण से वे केवल काम करते हैं यदि fragकोई फ़ाइल है, तो नहीं directory


1
gci संतान प्राप्ति के लिए एक उपनाम है। एक निर्देशिका के बच्चे इसकी सामग्री हैं। Gci को gi से बदलें और यह दोनों के लिए काम करना चाहिए।
zdan

2
गेट-आइटम ने अच्छी तरह से काम किया। लेकिन फिर से, इस दृष्टिकोण की आवश्यकता है कि फ़ोल्डर्स मौजूद हैं।
पीटर लिलेवॉल्ड

1

एक फंक्शन बनाएं। यह फ़ंक्शन एक पथ को सामान्य करेगा जो आपके सिस्टम पर मौजूद नहीं है और साथ ही ड्राइव अक्षर भी नहीं जोड़ता है।

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

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

उदाहरण के लिए:

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

RegEx में मदद के लिए थैंक्स ऑलिवर स्कडलिच को जाता है।


ध्यान दें somepaththing\.\filename.txtकि यह पथों के लिए काम नहीं करता है क्योंकि यह उस एकल बिंदु को रखता है
मार्क शुल्त्स

1

यदि पथ में एक क्वालीफायर (ड्राइव अक्षर) शामिल है, तो x0n का पॉवर्सशेल का उत्तर: उस पथ को हल करें जो मौजूद नहीं हो सकता है? पथ को सामान्य करेगा। यदि पथ में क्वालीफायर शामिल नहीं है, तो यह अभी भी सामान्यीकृत हो जाएगा, लेकिन वर्तमान निर्देशिका के सापेक्ष पूरी तरह से योग्य पथ को लौटा देगा, जो कि आप नहीं चाहते हैं।

$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

यदि आपको .. भाग से छुटकारा पाने की आवश्यकता है, तो आप एक System.IO.DirectoryInfo ऑब्जेक्ट का उपयोग कर सकते हैं। कंस्ट्रक्टर में 'फ्रेड \ मेंढक .. \' का प्रयोग करें। FullName गुण आपको सामान्यीकृत निर्देशिका नाम देगा।

एकमात्र दोष यह है कि यह आपको पूरा रास्ता देगा (उदाहरण के लिए c: \ test \ fred \ frag)।


0

यहाँ टिप्पणियों के समीचीन भागों को इस तरह संयोजित किया गया है कि वे सापेक्ष और निरपेक्ष पथों को एकीकृत करते हैं:

[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

-1

खैर, एक तरीका यह होगा:

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

रुको, शायद मैं सवाल गलत समझ सकता हूं। आपके उदाहरण में, क्या मेंढक का एक सबफ़ोल्डर है?


"मेंढक का एक सबफ़ोल्डर है?" नहीं। .. मतलब एक स्तर ऊपर जाना। फ़्रेड में एक उप-फ़ोल्डर (या फ़ाइल) है।
डैन-जीएफ ३१'०१
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.