2

Consider the following situation: I have a parameter, or a config variable, that sets the output directory of a script. Obviously, this parameter should also be able to be absolute:

RepoBackup.ps1 -OutputDirectory .\out
RepoBackup.ps1 -OutputDirectory D:\backup

In the script, I use (Get-Item -Path './').FullName in combination with Join-Path to determine the absolute path of my output directory, because I might need to use Set-Location to change the current directory - which makes working with relative paths complicated.

But:

Join-Path C:\code\ .\out  # => C:\code\.\out  (which is exactly what i need)
Join-Path C:\code\ D:\    # => C:\code\D:\    (which is not only not what i need, but invalid)

I considered using Resolve-Path and do something like Resolve-Path D:\backup, but if the directory doesn't exist (yet), this yields an error about not being able to find the path.

So, how can I get the absolute path of my $OutputDirectory, accepting both absolute and relative input, as well as paths that don't yet exist?

sk22
  • 837
  • 1
  • 10
  • 21

2 Answers2

1

This function did the job for me:

function Join-PathOrAbsolute ($Path, $ChildPath) {
    if (Split-Path $ChildPath -IsAbsolute) {
        Write-Verbose ("Not joining '$Path' with '$ChildPath'; " +
            "returning the child path as it is absolute.")
        $ChildPath
    } else {
        Write-Verbose ("Joining path '$Path' with '$ChildPath', " +
            "child path is not absolute")
        Join-Path $Path $ChildPath
    }
}

# short version, without verbose messages:

function Join-PathOrAbsolute ($Path, $ChildPath) {
  if (Split-Path $ChildPath -IsAbsolute) { $ChildPath }
  else { Join-Path $Path $ChildPath }
}
Join-PathOrAbsolute C:\code .\out  # => C:\code\.\out (just the Join-Path output)
Join-PathOrAbsolute C:\code\ D:\   # => D:\ (just the $ChildPath as it is absolute)

It just checks whether the latter path is absolute and returns it if it is, otherwise it just runs Join-Path on both $Path and $ChildPath. Note that this doesn't consider the base $Path to be relative, but for my use case this is perfectly enough. (I use (Get-Item -Path './').FullName as the base path, which is absolute anyway.)

Join-PathOrAbsolute .\ D:\    # => D:\
Join-PathOrAbsolute .\ .\out  # => .\.\out

Note that while .\.\ and C:\code\.\out does indeed look weird, it is valid and resolves to the correct path. It's just the output of PowerShell's integrated Join-Path function, after all.

sk22
  • 837
  • 1
  • 10
  • 21
1

You can use the .Net Path.Combine method, which does exactly what you want.

[IO.Path]::Combine('C:\code\', '.\out')  # => 'C:\code\.\out'
[IO.Path]::Combine('C:\code\', 'D:\')    # => 'D:\'
Steve
  • 183
  • 10