У меня есть файл 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 должны отражать правильный регистр.
Вместо того, чтобы указывать точное имя, просто используйте подстановочный знак:
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
}
}}