Назад | Перейти на главную страницу

Получить имя файла в случае, хранящемся на диске?

У меня есть файл README.TXT. Если я введу следующую команду:

PS> Get-Item ReadMe.txt

... затем возвращается "ReadMe.txt". Я хочу узнать настоящее имя файла на диске, включая регистр. Как мне заставить его вернуть "README.TXT"?

Я спрашиваю, потому что я пытаюсь отследить проблему с нечувствительными к регистру именами файлов в Windows по сравнению с чувствительными к регистру файлами в системе Unix, и я хочу получить реальный регистр, используемый в окне Windows.

Более детально: У меня есть список файлов (хранящихся в .CSPROJ файл), которые находятся в другом регистре, чем те, которые хранятся на диске. Я хочу быть уверенным, что они совпадают. Например: если .CSPROJ в файле написано «ReadMe.txt», но файл на диске - «README.TXT», иногда при редактировании файла в Visual Studio файл переписывается как «ReadMe.txt», что затем сбивает Perforce, поскольку он чувствителен к регистру, и имя_файла больше не имеет ожидаемого регистра. Я хочу написать сценарий, который обнаруживает несовпадающие имена файлов, чтобы я мог что-то с ними сделать, прежде чем это вызовет проблему.

Вот что я придумал:

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

    $result = Get-ChildItem $parent | where { $_ -like $leaf }
    $result.Name
}

Следующая функция PowerShell основана на элегантном решении для C # другого разработчика по адресу https://stackoverflow.com/a/11436327/854680.

Так как Get-ChildItem не используется, это намного быстрее, что имеет значение при вызове этой функции десятки или сотни раз.

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

Основная часть работы с файловой системой в PowerShell выполняется некоторыми классами .NET в пространстве имен System.IO. Если вы знаете путь (например, в вашем примере, вы перешли в папку файловой системы, в которой находится ваш предметный файл), то вы можете использовать что-то вроде

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

Поскольку базовые классы .NET чувствительны к регистру, результирующий вывод имен ваших файлов также будет чувствителен к регистру.

Для справки: в этом примере вы вызываете метод GetFiles объекта System.IO.DirectoryInfo, который представляет текущий рабочий каталог ($ pwd). Вы можете изменить это так, чтобы указывать на каталог, содержащий файл, который вам нужен для подтверждения корпуса.

Свойства Name, BaseName и FullName должны отражать правильный регистр.

Подробнее о методе GetFiles.

Вместо того, чтобы указывать точное имя, просто используйте подстановочный знак:

PS> Get-Item ReadMe.tx?

это должно вернуться README.TXT

Самый быстрый способ, который я могу придумать, - это заменить все косые черты на * \, а затем вызвать Get-Item, и, чтобы убедиться, что вы не получаете дубликатов, выберите тот, который без учета регистра равен указанному вами.

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

Вот мой быстрый и грязный (без проверки) подход:

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