DarkLite1
DarkLite1

Reputation: 14735

Accept a path as literal path, relative path or relative to the script root

Currently we want to cover these 3 cases when retrieving file information:

To cover these 3 cases we created the following function:

Function Get-FilePathItemHC {
    Param (
        [Parameter(Mandatory)]
        [String]$Path
    )

    $Params = @(
        @{
            # Path relative to the script root
            LiteralPath = Join-Path -Path $PSScriptRoot -ChildPath $Path
        }
        @{
            # Literal or path relative to the present work directory
            Path = $Path
        }
    )

    $Item = $null

    foreach ($P in $Params) {
        if ($Item = Get-Item @P -ErrorAction Ignore) {
            $Item
            Break
        }
    }

    if (-not $Item) {
        throw "Cannot find path '$Path' because it does not exist."
    }
}

Is this the right way of doing it? It seems like we're reinventing the wheel here.

Upvotes: 0

Views: 585

Answers (1)

codewario
codewario

Reputation: 21468

Make your -Path Parameter a System.IO.FileInfo object, and just pass in a relative path as the parameter. The file object will resolve with either a relative or full path, then you can use $path.FullName to reference the full path to the file.

Function Get-FilePathItemHC {
    Param (
        [Parameter(Mandatory)]
        [ValidateScript({ $_.Exists })]
        [System.IO.FileInfo]$Path
    )

    # The ValidateScript attribute makes sure the file you passed in exists
    # so your validation code no longer is required
}

If you want to handle both directories and files, you would want to have two separate variables in this case as directory paths would become a System.IO.DirectoryInfo object, but you can make the arguments mutually exclusive:

Function Get-FilePathItemHC {
Param (
    [Parameter(Mandatory=$true, ParameterSetName="FilePath")]
    [ValidateScript({ $_.Exists })]
    [System.IO.FileInfo]$FilePath,
    [Parameter(Mandatory=$true, ParameterSetName="DirectoryPath")]
    [ValidateScript({ $_.Exists })]
    [System.IO.DirectoryInfo]$DirectoryPath
)

  $Path = $FilePath
  if( $DirectoryPath ) {
    $Path = $DirectoryPath
  }

  # The ValidateScript attribute makes sure the file you passed in exists
  # so your validation code no longer is required
}

Get-FilePathItemHC -Path .\path\to\file.txt

Get the relative path from $PSScriptRoot

I'm not sure why you need the path relative to $PSScriptRoot if you already have the full path to the file, but after getting the System.IO.FileInfo or System.IO.DirectoryInfo object, you can use Resolve-Path from $PSScriptRoot to get the relative path from that directory:

$file = Get-FilePathItemHC -Path .\path\to\file.txt
Push-Location $PSScriptRoot
$relativeFromScriptRootPath = Resolve-Path -Relative $file
Pop-Location

Push-Location and Pop-Location treat the location as a stack. The push operation sets a new location and adds it to the stack, and the pop operation removes the last added location from the stack and places you at the next most recent location. Works a bit like cd - on Linux if you're familiar.

Resolve-Path will return a file path, and the -Relative switch will return a path relative to your current directory. You cannot pass in an alternate directory to resolve from, which is why we change the location to run this.

Upvotes: 1

Related Questions