Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get a path with the correct (canonical) case in PowerShell?

Tags:

powershell

I have a script that accepts a directory as an argument from the user. I'd like to display the name of the directory path as it is displayed in Windows. I.e.,

PS C:\SomeDirectory> cd .\anotherdirectory
PS C:\AnotherDirectory> . .\myscript.ps1 "c:\somedirectory"
C:\SomeDirectory

How do I retrieve "C:\SomeDirectory" when given "c:\somedirectory"?

like image 769
Glenn Avatar asked Aug 25 '11 18:08

Glenn


People also ask

How do I get the file path in PowerShell?

To get the full path of the file in PowerShell, use the Get-ChildItem to get files in the directory and pass the output to foreach-object to iterate over the file and get the full name of the file.

What is meant by canonical path?

The canonical path is always an absolute and unique path. If String pathname is used to create a file object, it simply returns the pathname.

What does resolve path do in PowerShell?

Description. The Resolve-Path cmdlet displays the items and containers that match the wildcard pattern at the location specified. The match can include files, folders, registry keys, or any other object accessible from a PSDrive provider.


3 Answers

The accepted answer only gets the correct case of the file. Parent paths are left with the case provided by the user. Here's my solution.

$getPathNameSignature = @'
[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Auto)]
public static extern uint GetLongPathName(
    string shortPath, 
    StringBuilder sb, 
    int bufferSize);

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError=true)]
public static extern uint GetShortPathName(
   string longPath,
   StringBuilder shortPath,
   uint bufferSize);
'@
$getPathNameType = Add-Type -MemberDefinition $getPathNameSignature -Name GetPathNameType -UsingNamespace System.Text -PassThru


function Get-PathCanonicalCase
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        # Gets the real case of a path
        $Path
    )

    if( -not (Test-Path $Path) )
    {
        Write-Error "Path '$Path' doesn't exist."
        return
    }

    $shortBuffer = New-Object Text.StringBuilder ($Path.Length * 2)
    [void] $getPathNameType::GetShortPathName( $Path, $shortBuffer, $shortBuffer.Capacity )

    $longBuffer = New-Object Text.StringBuilder ($Path.Length * 2)
    [void] $getPathNameType::GetLongPathName( $shortBuffer.ToString(), $longBuffer, $longBuffer.Capacity )

    return $longBuffer.ToString()
}

I've integrated the above code into Resolve-PathCase, part of the Carbon PowerShell module. Disclaimer: I'm the owner/maintainer of Carbon.

like image 107
Aaron Jensen Avatar answered Nov 15 '22 09:11

Aaron Jensen


This should work:

function Get-PathCanonicalCase {
    param($path)

    $newPath = (Resolve-Path $path).Path
    $parent = Split-Path $newPath

    if($parent) {
        $leaf = Split-Path $newPath -Leaf

        (Get-ChildItem $parent| Where-Object{$_.Name -eq $leaf}).FullName
    } else {
        (Get-PSDrive ($newPath -split ':')[0]).Root
    }
}
like image 23
Rynant Avatar answered Nov 15 '22 09:11

Rynant


I found a different and simpler approach using PowerShell wild cards.

 $canonicalCasePath = Get-ChildItem -Path $wrongCasingPath.Replace("\","\*") | Where FullName -IEQ $wrongCasingPath | Select -ExpandProperty FullName
  • The first part of the pipe replaces all backslashes in the path by backslash and asterisk \\* and return all matching files
  • The where part makes sure that only the desired file is returned and not any other potential match. IEQ is case insesitive equal
  • The last select part extracts canonical case path of the file
like image 43
honzajscz Avatar answered Nov 15 '22 07:11

honzajscz