Lalzy
Lalzy

Reputation: 97

Convert relative path into absolute string path

I need to store a relative path, as an absolute path in a file. I've tried quite a few things, including:

$path = (resolve-path -path "C:\Folder\..\Folder2").Path.ToString()

Add-content "d:\textfile.txt" $path

Which leaves an empy file. So I'm stumped.

Upvotes: 5

Views: 2712

Answers (2)

mklement0
mklement0

Reputation: 437428

The simplest way to resolve a relative and/or non-normalized path (one with components such as .. and .) to a normalized full path, directly as a string, is to use the Convert-Path cmdlet:

Convert-Path -LiteralPath "C:\Folder\..\Folder2"

In the context of your command (note that Add-Content appends to a preexisting target file; to write the given content only, use Set-Content):

Add-Content "d:\textfile.txt" (Convert-Path -LiteralPath "C:\Folder\..\Folder2")

Note:

  • Unlike Resolve-Path, Convert-Path returns a file-system-native path, which is generally preferable.

    • This means that file-system paths that are based on PowerShell-only drives (created with New-PSDrive) are resolved to the underlying native file-system location, as understood by outside processes too. (E.g., if MyDrive: is mapped to C:\Projects, Convert-Path -LiteralPath MyDrive:\Foo returns C:\Projects\Foo)
  • Like Resolve-Path, Convert-Path requires that the item the path refers to exist - which is an unfortunate limitation, discussed in GitHub issue #2993.


If the input path refers to a nonexistent file or directory:

.NET offers the [System.IO.Path]::GetFullPath() method, which offers the same functionality also for nonexistent paths.

The challenge is that relative paths are resolved to .NET's current (working) directory, which usually differs from PowerShell's - see this answer for more information.

In PowerShell (Core) 7+, you can work around this problem by specifying the reference path (to resolve the relative path against) explicitly:

# Note: The 'System.' prefix in the type literal is optional.
[IO.Path]::GetFullPath("C:\Folder\..\Folder2", $PWD.ProviderPath)

The automatic $PWD variable refers to PowerShell's current location (directory). Note the use of .ProviderPath, which again ensures uses of a file-system-native path. For maximum robustness - to guard agains the case where the current PowerShell location isn't a file-system location - use (Get-Location -PSProvider FileSystem).ProviderPath.

In Windows PowerShell, where this method overload isn't available, a more cumbersome approach is needed, as suggested by Theo:

# Note: If you don't need to *normalize* the path (resolving . and .. components), 
#       the [IO.Path]::Combine() call alone is enough.
[IO.Path]::GetFullPath(
  [IO.Path]::Combine($PWD.ProviderPath, "C:\Folder\..\Folder2")
)
  • [IO.Path]::Combine() conditionally combines the first path given with the second one, if the latter is relative - otherwise, the latter is used as-is.

  • [IO.Path]::GetFullPath() then ensures normalization of the resulting full path (to resolve any . and .. components); if that isn't required, just calling [IO.Path]::Combine() will do.

Upvotes: 6

Doug
Doug

Reputation: 7057

If your path is not existent yet, I found either of the following to work successfully:

[IO.Path]::GetFullPath(".\abc")
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath(".\abc.txt")

Upvotes: 0

Related Questions