0

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

  • A literal path
  • A relative path, relative to the script root
  • A relative path, relative to the present working directory

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.

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you @Bender but the thing I'm trying to find out is the path relative to the script root folder, a regular relative path or a literal path. You're code wont cover the first case I'm afraid.
See my updated answer for using Resolve-Path to get the relative path from $PSScriptRoot
Never heard about Push-Location and Pop-Location. Great tip, thanks! :)
In my $profile, I've actually aliasesd cd to Push-Location and aliased pd to Pop-Location. My solution to the lack of cd - without having to use the full cmdlet names or longer aliases of pushd and popd

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.