9

I have a file README.TXT. If I issue the following command:

PS> Get-Item ReadMe.txt

...then it returns "ReadMe.txt". I want to find out the actual name of the file on disk, including case. How do I get it to return "README.TXT"?

I ask because I'm trying to track down a problem with case-insensitive filenames on Windows versus case-sensitive files on a Unix box, and I want to get the actual case used on the Windows box.

More detail: I have a list of files (stored in a .CSPROJ file) which are in a different case from those stored on disk. I want to be sure that they match. For example: if the .CSPROJ file says "ReadMe.txt", but the file on disk is "README.TXT", sometimes editing the file in Visual Studio rewrites the file as "ReadMe.txt", which then confuses Perforce, because it's case-sensitive, and the filename no longer has the case it was expecting. I want to write a script that spots the mismatched filenames, so that I can do something about them before it causes a problem.

4

Here's what I came up with:

function Get-ActualFileName($file) {
    $parent = Split-Path $file
    $leaf = Split-Path -Leaf $file

    $result = Get-ChildItem $parent | where { $_ -like $leaf }
    $result.Name
}
1
  • Thanks for this. But I need this for the FullName. What can I change? – masterxilo Aug 20 '19 at 20:11
2

Rather than specifying the exact name, just use a wildcard:

PS> Get-Item ReadMe.tx?

this should return README.TXT

2
  • Better hope a file Readme.tx0 does not exist – scobi Dec 31 '16 at 14:48
  • That worked. You can also use wildcards in the directory names when specifying full path to get the correct names for the directories as well. – Tolga Apr 3 at 23:54
2

The following PowerShell function is based on another developer's elegant solution for C# at https://stackoverflow.com/a/11436327/854680.

Because Get-ChildItem is not used, it is much faster, which makes a difference when calling this function dozens or hundreds of times.

function Get-FileNameTrueCase {
    Param (
        [parameter(Mandatory=$true)]
        [string]
        $DirOrFile
    )

    if (-not ( (Test-Path $DirOrFile -PathType Leaf) -or (Test-Path $DirOrFile -PathType Container) ) ) {
        # File or directory not found
        return $DirOrFile
    }

    $TruePath = "";
    foreach ($PathPart in $DirOrFile.Split("\")) {
        if ($TruePath -eq "") {
            $TruePath = $PathPart.ToUpper() + "\";
            continue;
        }
        $TruePath = [System.IO.Directory]::GetFileSystemEntries($TruePath, $PathPart)[0];
    }
    return $TruePath;
}
1
  • Thanks for this variant which outputs a full path. Basically, this does the same as shift-right-click on a file in explorer, and selecting "copy as path". – masterxilo Aug 20 '19 at 20:12
1

The bulk of the file system work in PowerShell is done by some .NET classes in the System.IO namespace. If you know the path (like in your example, you've navigated to the file system location where your subject file is located), then you could use something like

(get-item $pwd).GetFiles('readme.txt')

Since the base .NET classes are case sensitive, the resulting output of your file names will be case sensitive as well.

For reference, the in the example, you are calling the GetFiles method of the System.IO.DirectoryInfo object that represents the current working directory ($pwd). You can change that to point to the directory containing the file you need to confirm the casing on.

The Name, BaseName, and FullName properties should all reflect the proper casing.

More info on the GetFiles method.

1
  • 1
    No, those return the path however you typed it. E.g.: cd C:\WINDOWS\SYSTEM32; (get-item $pwd).GetFiles(".\w32time.dll").FullName # Prints "C:\WINDOWS\SYSTEM32\.\w32time.dll" which is not only cased wrong, but also has a \.\ in it because that's how I typed it.... – Jaykul Sep 28 '13 at 4:21
0

The fastest way I can think of is to replace all the slashes with *\ and then call Get-Item, and to make sure you get no dupes, pick the one that's case-insensitively equal to the one you specified.

$Path = Get-Item "$($Path.TrimEnd('\/') -replace "(?<!:)(\\|/)", '*$1')*" |
           Where { $_.FullName -ieq $Path } | Convert-Path
0

Here's my quick and dirty (no validation) approach:

Function Resolve-PathName { [CmdletBinding()]
Param(
    [Parameter(ValueFromPipelinebyPropertyName,ValueFromPipeline,Mandatory)]
    [Alias("Fullname")]
    [string] $File,
    [switch] $Cygpath
) Process {
    $cur = $File
    $stack = New-Object System.Collections.Stack

    while ( $cur.length -gt 0) {
        $stack.Push( (split-path $cur -Leaf) )
        $cur = split-path $cur
    }
    $cur = get-item ($stack.pop())
    while ( $stack.Count -gt 0) {
        $cur = $cur | Get-ChildItem -Filter ($stack.Pop())
    }
    if ($Cygpath) {
        cygpath $cur.Fullname
    } else {
        $cur.Fullname
    }
}}
1
  • Why should there be CygWin on a production machine? – Deer Hunter Nov 4 '15 at 8:21
0

I'll provide an answer in two parts. 1) If you only care about case of the filename and 2) If you care about the case of the full file path.

1. Filename case

The easiest way to get Powershell to give you the correct case of a filename is to have it search for the filename. We can accomplish that by using a RegEx character class (set of valid characters) for the first letter of the filename.

For ReadMe.txt we can search by [R]eadMe.txt. The RegEx character class is intended to be typically used for multiple possible characters, but in this case we simply use one to always obtain a single match:

Get-Item [R]eadMe.txt | Select Name

The above returns the filename with the correct case:

README.TXT

If the filename is being dynamically provided or looked up you can use the following simple RegEx statement to surround the first characters with brackets:

"ReadMe.txt" -replace "^(.)", '[$1]'

This returns:

[R]eadMe.txt

Example usage to get the filename with correct case:

$filename = "ReadMe.txt"
Get-Item ($filename -replace "^(.)", '[$1]') | Select Name

The above returns the filename with the correct case:

README.TXT

2. Full File Path case

The basic concept here stays the same as part one. We will have Powershell give us the correct case of all segments of the full file path by having it search for each segment.

Similar to our earlier approach, the easiest way to do that is to surround the first character of each segment by brackets.

If the file name is known, for example:

c:\WINDOWS\systeM32\driverS\ETC\hosTS

You can get the correct case for the full path by introducing a RegEx character class (set of valid characters) for the first letter of each segment:

Get-Item c:\[W]INDOWS\[s]ysteM32\[d]riverS\[E]TC\[h]osTS | Select FullName

This on my system returns the full path and filename with the correct case:

C:\Windows\System32\drivers\etc\hosts

If the full file path is being dynamically passed in or looked up, you can simply use the following RegEx statement to automatically surround the first character of each segment with brackets:

$FullFilePath = "c:\WINDOWS\systeM32\driverS\ETC\hosts"
$FullFilePath -replace "(\\|/)(.)", '$1[$2]'

This returns:

c:\[W]INDOWS\[s]ysteM32\[d]riverS\[E]TC\[h]osts

Example usage to get the full file path with correct case:

$FullFilePath = "c:\WINDOWS\systeM32\driverS\ETC\hosts"
Get-Item ($FullFilePath -replace "(\\|/)(.)", '$1[$2]') | Select FullName

The above returns the full file path with the correct case:

C:\Windows\System32\drivers\etc\hosts

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.