Functions/GenXdev.FileSystem/Expand-Path.ps1
################################################################################ <# .SYNOPSIS Expands any given file reference to a full pathname. .DESCRIPTION Expands any given file reference to a full pathname, with respect to the user's current directory. Can optionally assure that directories or files exist. .PARAMETER FilePath The file path to expand to a full path. .PARAMETER CreateDirectory Will create directory if it does not exist. .PARAMETER CreateFile Will create an empty file if it does not exist. .EXAMPLE Expand-Path -FilePath ".\myfile.txt" -CreateFile .EXAMPLE ep ~\documents\test.txt -CreateFile #> function Expand-Path { [CmdletBinding()] [Alias("ep")] param( ######################################################################## [Parameter( Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = "Path to expand" )] [ValidateNotNullOrEmpty()] [string] $FilePath, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = "Will create directory if it does not exist" )] [switch] $CreateDirectory, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = "Will create an empty file if it does not exist" )] [switch] $CreateFile, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = "Will delete the file if it already exists" )] [switch] $DeleteExistingFile, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = "Will force the use of a specific drive" )] [char] $ForceDrive = '*', ######################################################################## [Parameter( Mandatory = $false, HelpMessage = "Will throw if file does not exist" )] [switch] $FileMustExist, ######################################################################## [Parameter( Mandatory = $false, HelpMessage = "Will throw if directory does not exist" )] [switch] $DirectoryMustExist ######################################################################## ) begin { # normalize path separators and remove double separators [string] $normalizedPath = $FilePath.Trim().Replace("\", [IO.Path]::DirectorySeparatorChar). Replace("/", [IO.Path]::DirectorySeparatorChar); if ($normalizedPath.StartsWith([IO.Path]::DirectorySeparatorChar + [IO.Path]::DirectorySeparatorChar)) { $normalizedPath = [IO.Path]::DirectorySeparatorChar + [IO.Path]::DirectorySeparatorChar + $normalizedPath.Substring(2).Replace( [IO.Path]::DirectorySeparatorChar + [IO.Path]::DirectorySeparatorChar, [IO.Path]::DirectorySeparatorChar ) if (($ForceDrive -ne '*') -and ("ABCDEFGHIJKLMNOPQRSTUVWXYZ".IndexOf(($ForceDrive -as [string]).ToUpperInvariant()) -ge 0)) { $i = $normalizedPath.IndexOf([IO.Path]::DirectorySeparatorChar, 2); $normalizedPath = $ForceDrive + ":" + ( $i -lt 0 ? ([IO.Path]::DirectorySeparatorChar) : $normalizedPath.Substring($i) ) } } else { $normalizedPath = $normalizedPath.Replace( [IO.Path]::DirectorySeparatorChar + [IO.Path]::DirectorySeparatorChar, [IO.Path]::DirectorySeparatorChar ) } # check if path ends with a directory separator $hasTrailingSeparator = $normalizedPath.EndsWith( [System.IO.Path]::DirectorySeparatorChar) -or $normalizedPath.EndsWith([System.IO.Path]::AltDirectorySeparatorChar) } process { # expand home directory if path starts with ~ if ($normalizedPath.StartsWith("~")) { if (($ForceDrive -ne '*') -and ("ABCDEFGHIJKLMNOPQRSTUVWXYZ".IndexOf(($ForceDrive -as [string]).ToUpperInvariant()) -ge 0)) { $i = $normalizedPath.IndexOf([IO.Path]::DirectorySeparatorChar, 1); $normalizedPath = $ForceDrive + ":" + ( $i -lt 0 ? [IO.Path]::DirectorySeparatorChar + "**" + [IO.Path]::DirectorySeparatorChar : ("\**" + $normalizedPath.Substring($i)) ) } else { $normalizedPath = Join-Path (Convert-Path ~) ` $normalizedPath.Substring(1) } } if ((($normalizedPath.Length -gt 1) -and ($normalizedPath.Substring(1, 1) -eq ":"))) { if (($ForceDrive -ne '*') -and ("ABCDEFGHIJKLMNOPQRSTUVWXYZ".IndexOf(($ForceDrive -as [string]).ToUpperInvariant()) -ge 0)) { $i = $normalizedPath.IndexOf([IO.Path]::DirectorySeparatorChar); $normalizedPath = $ForceDrive + ":" + [IO.Path]::DirectorySeparatorChar + (($i -eq -1 -and $normalizedPath.Length -gt 2) -or $i -eq 2 ? "**" + [IO.Path]::DirectorySeparatorChar : "") + $normalizedPath.Substring(2) } else { if (($normalizedPath.Length -lt 3) -or ($normalizedPath.Substring(2, 1) -ne [System.IO.Path]::DirectorySeparatorChar)) { Push-Location $normalizedPath.Substring(0, 2) try { $normalizedPath = "$(Get-Location)$([IO.Path]::DirectorySeparatorChar)$($normalizedPath.Substring(2))" $normalizedPath = [System.IO.Path]::GetFullPath($normalizedPath) } finally { Pop-Location } } } } # handle absolute paths (drive letter or UNC) if ($normalizedPath.StartsWith([IO.Path]::DirectorySeparatorChar + [IO.Path]::DirectorySeparatorChar)) { try { $normalizedPath = [System.IO.Path]::GetFullPath($normalizedPath) } catch { Write-Verbose "Failed to normalize path, keeping original" } } else { if (($ForceDrive -ne '*') -and ("ABCDEFGHIJKLMNOPQRSTUVWXYZ".IndexOf(($ForceDrive -as [string]).ToUpperInvariant()) -ge 0)) { if ($normalizedPath.Length -lt 2 -or $normalizedPath.Substring(1, 1) -ne ":") { $newPath = $ForceDrive + ":" + [IO.Path]::DirectorySeparatorChar; while ($normalizedPath.StartsWith(".")) { $i = $normalizedPath.IndexOf([IO.Path]::DirectorySeparatorChar); if ($i -lt 0) { $normalizedPath = "" } else { $normalizedPath = $normalizedPath.Substring($i + 1) } } if ($normalizedPath.StartsWith([IO.Path]::DirectorySeparatorChar)) { $newPath += $normalizedPath } else { $newPath += "**" + [IO.Path]::DirectorySeparatorChar + $normalizedPath } $normalizedPath = $newPath } } # handle relative paths try { $normalizedPath = [System.IO.Path]::GetFullPath( [System.IO.Path]::Combine($pwd, $normalizedPath)) } catch { $normalizedPath = Convert-Path $normalizedPath } } # handle directory/file creation if requested if ($DirectoryMustExist -or $FileMustExist) { # get directory path accounting for trailing separator $directoryPath = if ($hasTrailingSeparator) { [IO.Path]::TrimEndingDirectorySeparator($normalizedPath) } else { [IO.Path]::TrimEndingDirectorySeparator( [System.IO.Path]::GetDirectoryName($normalizedPath)) } # create directory if it doesn't exist if ($DirectoryMustExist -and (-not [IO.Directory]::Exists($directoryPath))) { throw "Directory does not exist: $directoryPath" } if ($FileMustExist -and (-not [IO.File]::Exists($normalizedPath))) { throw "File does not exist: $normalizedPath" } } # handle directory/file creation if requested if ($CreateDirectory -or $CreateFile) { # get directory path accounting for trailing separator $directoryPath = if ($hasTrailingSeparator) { [IO.Path]::TrimEndingDirectorySeparator($normalizedPath) } else { [IO.Path]::TrimEndingDirectorySeparator( [System.IO.Path]::GetDirectoryName($normalizedPath)) } # create directory if it doesn't exist if (-not [IO.Directory]::Exists($directoryPath)) { $null = [IO.Directory]::CreateDirectory($directoryPath) Write-Verbose "Created directory: $directoryPath" } } # delete existing file if requested if ($DeleteExistingFile -and [IO.File]::Exists($normalizedPath)) { # verify path doesn't point to existing directory if ([IO.Directory]::Exists($normalizedPath)) { throw "Cannot create file: Path refers to an existing directory" } if (-not (Remove-ItemWithFallback -Path $normalizedPath)) { throw "Failed to delete existing file: $normalizedPath" } Write-Verbose "Deleted existing file: $normalizedPath" } # handle file creation if requested if ($CreateFile) { # verify path doesn't point to existing directory if ([IO.Directory]::Exists($normalizedPath)) { throw "Cannot create file: Path refers to an existing directory" } # create empty file if it doesn't exist if (-not [IO.File]::Exists($normalizedPath)) { $null = [IO.File]::WriteAllText($normalizedPath, "") Write-Verbose "Created empty file: $normalizedPath" } } # clean up trailing separators except for root paths while ([IO.Path]::EndsInDirectorySeparator($normalizedPath) -and $normalizedPath.Length -gt 4) { $normalizedPath = [IO.Path]::TrimEndingDirectorySeparator($normalizedPath) } return $normalizedPath } end { } } ################################################################################ |