Pathfinder/Get-CanonicalPath.ps1
function Get-CanonicalPath { [CmdletBinding()] [OutputType([string])] param ( [Alias("PSPath")] [Parameter(Mandatory)] [string] $Path ) # Determine provider path $provider = $null $drive = $null $providerPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path, [ref] $provider, [ref] $drive) # Guard against unsupported providers if ($provider -ne (Get-PSProvider -PSProvider "FileSystem")) { $message = "$($MyInvocation.MyCommand.Name) does not support the $provider provider" $parameters = @{ Exception = [System.NotSupportedException]::new($message) ErrorId = "PSProviderNotSupported" Message = $message } Write-Error @parameters } # Get the root-relative provider path $index = $drive.Root.Length $length = $providerPath.Length - $index $rootRelativeProviderPath = $providerPath.Substring($index, $length) # Return the drive root when there is no root-relative provider path if ($rootRelativeProviderPath -eq ([string]::Empty)) { return $drive.Root } # Split the root-relative provider path on directory separators $separator = [System.IO.Path]::DirectorySeparatorChar $segments = $rootRelativeProviderPath.Split($separator) # Start with the drive root ("C:\" or "D:\" etc., or "/") $canonicalPath = $drive.Root # Loop over the segments of the root-relative provider path for ($i = 0; $i -lt $segments.Length; $i++) { $segment = $segments[$i] # First segment can be empty if the drive root is "/" if ($segment -eq ([string]::Empty)) { continue } # Check if an item with the name of the segment exists $segmentPath = Join-Path $canonicalPath $segment if (Test-Path $segmentPath) { # Find matching items $items = @(Get-ChildItem $canonicalPath -Filter $segment -Force) # Check for indicator that the file system is case-insensitive if ($items.Length -eq 1 -and $items[0].Name -cne $segment) { # Fix the segment using the name of the matching item $segment = $items[0].Name } } $canonicalPath = Join-Path $canonicalPath $segment } return $canonicalPath } |