120

I have two paths:

fred\frog

and

..\frag

I can join them together in PowerShell like this:

join-path 'fred\frog' '..\frag'

That gives me this:

fred\frog\..\frag

But I don't want that. I want a normalized path without the double dots, like this:

fred\frag

How can I get that?

dan-gph
  • 16,301
  • 12
  • 61
  • 79
  • 1
    Is frag a subfolder of frog? If not then combining the path would get you fred\frog\frag. If it is then that is a very different question. – EBGreen Jan 30 '09 at 14:41

14 Answers14

117

You can expand ..\frag to its full path with resolve-path:

PS > resolve-path ..\frag 

Try to normalize the path using the combine() method:

[io.path]::Combine("fred\frog",(resolve-path ..\frag).path)
abatishchev
  • 98,240
  • 88
  • 296
  • 433
Shay Levy
  • 121,444
  • 32
  • 184
  • 206
  • What if your path is `C:\Windows` vs `C:\Windows\ ` the same path but two different results – Joe Phillips May 25 '12 at 17:01
  • 3
    The parameters for `[io.path]::Combine` are reversed. Better yet, use the native `Join-Path` PowerShell command: `Join-Path (Resolve-Path ..\frag).Path 'fred\frog'` Also note that, at least as of PowerShell v3, `Resolve-Path` now supports the `-Relative` switch for resolving to a path relative to the current folder. As mentioned, `Resolve-Path` only works with existing paths, unlike `[IO.Path]::GetFullPath()`. – mklement0 Mar 11 '13 at 23:41
  • 1
    @mklement0 the `-Relative` switch for `Resolve-Path` seems to have been added in PSv2 according to this online reference: http://adamringenberg.com/powershell2/resolve-path/ – Prid Sep 01 '21 at 23:26
  • @JoePhillips [This](https://stackoverflow.com/questions/71796053/how-to-normalize-and-compare-paths-in-powershell) is the function I am using for this, maybe this can be helpful for you aswell – Kevin Holtkamp May 04 '22 at 14:56
  • And it doesn't handle non-existent paths as well. As you can see here on the tests https://gist.github.com/Luiz-Monad/d5aea290087a89c070da6eec84b33742#test-only_resolve_path – Luiz Felipe Oct 26 '22 at 16:34
91

You can use a combination of $pwd, Join-Path and [System.IO.Path]::GetFullPath to get a fully qualified expanded path.

Since cd (Set-Location) doesn't change the process current working directory, simply passing a relative file name to a .NET API that doesn't understand PowerShell context, can have unintended side-effects, such as resolving to a path based off the initial working directory (not your current location).

What you do is you first qualify your path:

Join-Path (Join-Path $pwd fred\frog) '..\frag'

This yields (given my current location):

C:\WINDOWS\system32\fred\frog\..\frag

With an absolute base, it is now safe to call the .NET API GetFullPath:

[System.IO.Path]::GetFullPath((Join-Path (Join-Path $pwd fred\frog) '..\frag'))

Which gives you the fully qualified path, with the .. correctly resolved:

C:\WINDOWS\system32\fred\frag

It's not complicated either, personally, I disdain the solutions that depend on external scripts for this, it's simple problem solved rather aptly by Join-Path and $pwd (GetFullPath is just to make it pretty). If you only want to keep only the relative part, you just add .Substring($pwd.Path.Trim('\').Length + 1) and voila!

fred\frag

UPDATE

Thanks to @Dangph for pointing out the C:\ edge case.

ComFreek
  • 29,044
  • 18
  • 104
  • 156
John Leidegren
  • 59,920
  • 20
  • 131
  • 152
  • The last step doesn't work if pwd is "C:\". In that case I get "red\frag". – dan-gph Mar 15 '13 at 01:13
  • @Dangph - Not sure I understand what you mean, the above appears to be working just fine? What version of PowerShell are you using? I'm using version 3.0. – John Leidegren Mar 15 '13 at 07:14
  • 1
    I mean the last step: `cd c:\; "C:\fred\frag".Substring((pwd).Path.Length + 1)` . It's not a big deal; just something to be aware of. – dan-gph Mar 16 '13 at 09:24
  • Ah, good catch, we could fix that by adding a trim call. Try `cd c:\; "C:\fred\frag".Substring((pwd).Path.Trim('\').Length + 1)`. It's getting kinda long though. – John Leidegren Mar 16 '13 at 13:21
  • 3
    Or, just use: $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath(".\nonexist\foo.txt") Works with non-existant paths too. "x0n" deserves the credit for this btw. As he notes, it resolves to PSPaths, not flilesystem paths, but if you're using the paths in PowerShell, who cares? http://stackoverflow.com/questions/3038337/powershell-resolve-path-that-might-not-exist – Joe the Coder Apr 09 '16 at 20:09
  • This has lots of edge cases, its better to use GetUnresolveProviderPath, as you can see here in the tests. https://gist.github.com/Luiz-Monad/d5aea290087a89c070da6eec84b33742#test-combine_pwd_then_getfullpath – Luiz Felipe Oct 26 '22 at 16:27
28

The accepted answer was a great help however it doesn't properly 'normalize' an absolute path too. Find below my derivative work which normalizes both absolute and relative paths.

function Get-AbsolutePath ($Path)
{
    # System.IO.Path.Combine has two properties making it necesarry here:
    #   1) correctly deals with situations where $Path (the second term) is an absolute path
    #   2) correctly deals with situations where $Path (the second term) is relative
    # (join-path) commandlet does not have this first property
    $Path = [System.IO.Path]::Combine( ((pwd).Path), ($Path) );

    # this piece strips out any relative path modifiers like '..' and '.'
    $Path = [System.IO.Path]::GetFullPath($Path);

    return $Path;
}
Sean Hanna
  • 331
  • 4
  • 4
  • 1
    Given all the different solutions, this one works for all different types of paths. As an example `[IO.Path]::GetFullPath()` doesn't correctly determine directory for a plain filename. – Jari Turkia Feb 07 '19 at 09:12
  • The proper solution for normalizing absolute paths is just using https://stackoverflow.com/questions/3038337/powershell-resolve-path-that-might-not-exist/3040982#3040982 – Luiz Felipe Oct 26 '22 at 16:41
  • The problem with this solution is that it will work with non-existing paths but only on existing drives. As you can see on this test results https://gist.github.com/Luiz-Monad/d5aea290087a89c070da6eec84b33742#test-only_unresolved_provider_path_from_pspath – Luiz Felipe Oct 26 '22 at 16:47
28

You could also use Path.GetFullPath, although (as with Dan R's answer) this will give you the entire path. Usage would be as follows:

[IO.Path]::GetFullPath( "fred\frog\..\frag" )

or more interestingly

[IO.Path]::GetFullPath( (join-path "fred\frog" "..\frag") )

both of which yield the following (assuming your current directory is D:\):

D:\fred\frag

Note that this method does not attempt to determine whether fred or frag actually exist.

Charlie
  • 44,214
  • 4
  • 43
  • 69
  • That's getting close, but when I try it I get "H:\fred\frag" even though my current directory is "C:\scratch", which is wrong. (It shouldn't do that according to the MSDN.) It gave me an idea however. I'll add it as an answer. – dan-gph Jan 31 '09 at 01:37
  • 9
    Your problem is that you need to set the current directory in .NET. `[System.IO.Directory]::SetCurrentDirectory(((Get-Location -PSProvider FileSystem).ProviderPath))` – JasonMArcher Aug 16 '11 at 04:47
  • 2
    Just to state it explicitly: `[IO.Path]::GetFullPath()`, unlike PowerShell's native `Resolve-Path`, works with non-existent paths as well. Its downside is the need to sync .NET's working folder with PS' first, as @JasonMArcher points out. – mklement0 Mar 11 '13 at 23:45
  • `Join-Path` causes an exception if are referring to a drive that does not exist. – Tahir Hassan Jun 23 '17 at 11:49
15

Any non-PowerShell path manipulation functions (such as those in System.IO.Path) will not be reliable from PowerShell because PowerShell's provider model allows PowerShell's current path to differ from what Windows thinks the process' working directory is.

Also, as you may have already discovered, PowerShell's Resolve-Path and Convert-Path cmdlets are useful for converting relative paths (those containing '..'s) to drive-qualified absolute paths but they fail if the path referenced does not exist.

The following very simple cmdlet should work for non-existant paths. It will convert 'fred\frog\..\frag' to 'd:\fred\frag' even if a 'fred' or 'frag' file or folder cannot be found (and the current PowerShell drive is 'd:').

function Get-AbsolutePath {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [string[]]
        $Path
    )

    process {
        $Path | ForEach-Object {
            $PSCmdlet.SessionState.Path.GetUnresolvedProviderPathFromPSPath($_)
        }
    }
}
Jason Stangroome
  • 4,459
  • 3
  • 33
  • 39
  • 3
    This doesn't work for non-existant paths where the drive letter doesn't exist e.g. I have no Q: drive. `Get-AbsolutePath q:\foo\bar\..\baz` fails even though it is a valid path. Well, depending on your definiton of a valid path. :-) FWIW, even the built-in `Test-Path -IsValid` fails on paths rooted in drives that don't exist. – Keith Hill Feb 13 '11 at 03:42
  • 3
    @KeithHill In other words, PowerShell considers a path on a non-existent root to be invalid. I think that's fairly reasonable since PowerShell uses the root to decide what kind of provider to use when working with it. E.g., `HKLM:\SOFTWARE` is a valid path in PowerShell, referring to the `SOFTWARE` key in the Local Machine registry hive. But to figure out if it's valid, it needs to figure out what the rules for registry paths are. – jpmc26 Nov 26 '18 at 20:00
  • you have to strip the drive using split-path, see my answer. – Luiz Felipe Aug 09 '22 at 21:11
  • Look here for a proper implementation https://gist.github.com/Luiz-Monad/d5aea290087a89c070da6eec84b33742#test-proper_drive_provider_path_from_pspath – Luiz Felipe Oct 26 '22 at 16:52
7

If the path includes a qualifier (drive letter) then x0n's answer to Powershell: resolve path that might not exist? will normalize the path. If the path doesn't include the qualifier, it will still be normalized but will return the fully qualified path relative to the current directory, which may not be what you want.

$p = 'X:\fred\frog\..\frag'
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($p)
X:\fred\frag

$p = '\fred\frog\..\frag'
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($p)
C:\fred\frag

$p = 'fred\frog\..\frag'
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($p)
C:\Users\WileCau\fred\frag
WileCau
  • 2,057
  • 1
  • 24
  • 34
3

This library is good: NDepend.Helpers.FileDirectoryPath.

EDIT: This is what I came up with:

[Reflection.Assembly]::LoadFrom("path\to\NDepend.Helpers.FileDirectoryPath.dll") | out-null

Function NormalizePath ($path)
{
    if (-not $path.StartsWith('.\'))  # FilePathRelative requires relative paths to begin with '.'
    {
        $path = ".\$path"
    }

    if ($path -eq '.\.')  # FilePathRelative can't deal with this case
    {
        $result = '.'
    }
    else
    {
        $relPath = New-Object NDepend.Helpers.FileDirectoryPath.FilePathRelative($path)
        $result = $relPath.Path
    }

    if ($result.StartsWith('.\')) # remove '.\'. 
    {
        $result = $result.SubString(2)
    }

    $result
}

Call it like this:

> NormalizePath "fred\frog\..\frag"
fred\frag

Note that this snippet requires the path to the DLL. There is a trick you can use to find the folder containing the currently executing script, but in my case I had an environment variable I could use, so I just used that.

dan-gph
  • 16,301
  • 12
  • 61
  • 79
  • I don't know why that got a downvote. That library really is good for doing path manipulations. It's what I ended up using in my project. – dan-gph Mar 01 '09 at 01:16
  • Minus 2. Still puzzled. I hope that people realize that it's easy to use .Net assemblies from PowerShell. – dan-gph May 28 '11 at 03:39
  • This doesn't seem like the best solution, but it is perfectly valid. – JasonMArcher Aug 10 '11 at 02:31
  • @Jason, I don't remember the details, but at the time it was the best solution because it was the only one that solved my particular problem. It's possible however that since then another, better solution has come along. – dan-gph Aug 16 '11 at 02:53
  • It probably got downvoted because you had good answers but chose to add a one-liner with a link and accepted it. – Louis Kottmann Mar 14 '13 at 10:01
  • @Baboon, it was the only solution that solved the problem, at that time at least. Maybe one of the other solutions is better now. I have unaccepted the answer now since it seems to bother people. But I still think it's a good solution. You just drop the DLL into the folder with the script and load it in. It has been working perfectly for four years now. I can look up the details of how load the assembly if anyone wants. – dan-gph Mar 14 '13 at 13:42
  • 3
    having a 3rd party DLL is a big drawback to this solution – Louis Kottmann Mar 14 '13 at 13:58
  • if it was at least a powershellgallery module – Luiz Felipe Aug 06 '22 at 16:56
3

If the path exists, and you don't mind returning an absolute path, you could use Join-Path with the -Resolve parameter:

Join-Path 'fred\frog' '..\frag' -Resolve
James T
  • 795
  • 9
  • 11
  • This always produces absolute normalized paths, and it does't work with non-existent paths. as you can see here in the tests I made https://gist.github.com/Luiz-Monad/d5aea290087a89c070da6eec84b33742#test-only_join_path_resolve – Luiz Felipe Oct 26 '22 at 16:30
1

Create a function. This function will normalize a path that does not exists on your system as well as not add drives letters.

function RemoveDotsInPath {
  [cmdletbinding()]
  Param( [Parameter(Position=0,  Mandatory=$true)] [string] $PathString = '' )

  $newPath = $PathString -creplace '(?<grp>[^\n\\]+\\)+(?<-grp>\.\.\\)+(?(grp)(?!))', ''
  return $newPath
}

Ex:

$a = 'fooA\obj\BusinessLayer\..\..\bin\BusinessLayer\foo.txt'
RemoveDotsInPath $a
'fooA\bin\BusinessLayer\foo.txt'

Thanks goes out to Oliver Schadlich for help in the RegEx.

M.Hubers
  • 21
  • 6
1

This gives the full path:

(gci 'fred\frog\..\frag').FullName

This gives the path relative to the current directory:

(gci 'fred\frog\..\frag').FullName.Replace((gl).Path + '\', '')

For some reason they only work if frag is a file, not a directory.

dan-gph
  • 16,301
  • 12
  • 61
  • 79
  • 1
    gci is an alias for get-childitem. The children of a directory are its contents. Replace gci with gi and it should work for both. – zdan Jan 31 '09 at 02:43
  • 2
    Get-Item worked nicely. But again, this approach requires that the folders exist. – Peter Lillevold May 15 '12 at 09:58
1

None of the answers are entirely acceptable for the following reasons.

  • It must support powershell providers.
  • It must work for paths that don't exist in drives that don't exist.
  • It must process ".." and ".", that's what a normalized path is.
  • No external libraries, and no regex.
  • It mustn't reroot the path, this means relative paths stay relative.

For the following reasons, I made a list of the expected results you get for each method listed here, as follows:


function tests {
    context "cwd" {
        it 'has no external libraries' {
            Load-NormalizedPath
        }
        it 'barely work for FileInfos on existing paths' {
            Get-NormalizedPath 'a\..\c' | should -be 'c'
        }
        it 'process .. and . (relative paths)' {
            Get-NormalizedPath 'a\b\..\..\c\.' | should -be 'c'
        }
        it 'must support powershell providers' {
            Get-NormalizedPath "FileSystem::\\$env:COMPUTERNAME\Shared\a\..\c" | should -be "FileSystem::\\$env:COMPUTERNAME\Shared\c"
        }
        it 'must support powershell drives' {
            Get-NormalizedPath 'HKLM:\Software\Classes\.exe\..\.dll' | should -be 'HKLM:\Software\Classes\.dll'
        }
        it 'works with non-existant paths' {
            Get-NormalizedPath 'fred\frog\..\frag\.' | should -be 'fred\frag'
        }
        it 'works with non-existant drives' {
            Get-NormalizedPath 'U:\fred\frog\..\frag\.' | should -be 'U:\fred\frag'
        }
        it 'barely work for direct UNCs' {
            Get-NormalizedPath "\\$env:COMPUTERNAME\Shared\a\..\c" | should -be "\\$env:COMPUTERNAME\Shared\c"
        }
    }
    context "reroot" {
        it 'doesn''t reroot subdir' {
            Get-NormalizedPath 'fred\frog\..\frag\.' | should -be 'fred\frag'
        }
        it 'doesn''t reroot local' {
            Get-NormalizedPath '.\fred\frog\..\frag\.' | should -be 'fred\frag'
        }
        it 'doesn''t reroot parent' {
            Get-NormalizedPath "..\$((Get-Item .).Name)\fred\frog\..\frag\." | should -be 'fred\frag'
        }
    }
    context "drive root" {
        beforeEach { Push-Location 'c:/' }
        it 'works on drive root' {
            Get-NormalizedPath 'fred\frog\..\..\fred\frag\' | should -be 'fred\frag\'
        }
        afterEach { Pop-Location }
    }
    context "temp drive" {
        beforeEach { New-PSDrive -Name temp -PSProvider FileSystem 'b:/tools' }
        it 'works on temp drive' {
            Get-NormalizedPath 'fred\frog\..\..\fred\frag\' | should -be 'fred\frag\'
        }
        it 'works on temp drive with absolute path' {
            Get-NormalizedPath 'temp:\fred\frog\..\..\fred\frag\' | should -be 'temp:\fred\frag\'
        }
        afterEach { Remove-PSDrive -Name temp }
    }
    context "unc drive" {
        beforeEach { Push-Location "FileSystem::\\$env:COMPUTERNAME\Shared\​" }
        it 'works on unc drive' {
            Get-NormalizedPath 'fred\frog\..\..\fred\frag\' | should -be 'fred\frag\'
        }
        afterEach { Pop-Location }
    }
}

The correct answer uses GetUnresolvedProviderPathFromPSPath, but it can't work on its own, if you try using it directly, you get those results. From this answer https://stackoverflow.com/a/52157943/1964796 .

$path = Join-Path '/' $path
$path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($path)
$path = $path.Replace($pwd.Path, '').Replace($pwd.Drive.Root, '')
pros: simple
cons: needs boilerplate to make it correct, doesn't work with other providers or non-ex drives.

 Context cwd
   [+] has no external libraries 4ms (1ms|3ms)
   [+] barely work for FileInfos on existing paths 3ms (2ms|0ms)
   [+] process .. and . (relative paths) 3ms (2ms|0ms)
   [-] must support powershell providers 4ms (3ms|1ms)
    Expected: 'FileSystem::\\LUIZMONAD\Shared\c'
    But was:  '\\LUIZMONAD\Shared\a\..\c'
               ^
   [-] must support powershell drives 14ms (4ms|10ms)
    Expected: 'HKLM:\Software\Classes\.dll'
    But was:  'Cannot find drive. A drive with the name '\HKLM' does not exist.'
               ^
   [+] works with non-existant paths 3ms (2ms|1ms)
   [-] works with non-existant drives 4ms (3ms|1ms)
    Expected: 'U:\fred\frag'
    But was:  'Cannot find drive. A drive with the name '\U' does not exist.'
               ^
   [-] barely work for direct UNCs 3ms (3ms|1ms)
    Expected: '\\LUIZMONAD\Shared\c'
    But was:  '\\LUIZMONAD\Shared\a\..\c'
               -------------------^
 Context reroot
   [+] doesn't reroot subdir 3ms (2ms|1ms)
   [+] doesn't reroot local 33ms (33ms|1ms)
   [-] doesn't reroot parent 4ms (3ms|1ms)
    Expected: 'fred\frag'
    But was:  '\fred\frag'
               ^
 Context drive root
   [+] works on drive root 5ms (3ms|2ms)
 Context temp drive
   [+] works on temp drive 4ms (3ms|1ms)
   [-] works on temp drive with absolute path 6ms (5ms|1ms)
    Expected: 'temp:\fred\frag\'
    But was:  'Cannot find drive. A drive with the name '\temp' does not exist.'
               ^
 Context unc drive
   [+] works on unc drive 6ms (5ms|1ms)
Tests completed in 207ms
Tests Passed: 9, Failed: 6, Skipped: 0 NotRun: 0

So, what we need to do is to strip the driver/provider/unc and then use the GetUnresolvedProviderPathFromPSPath and then put the driver/provider/unc back. Unfortunately GetUPPFP depends on the current pwd state, but we at least aren't changing it.

$path_drive = [ref] $null
$path_abs = $ExecutionContext.SessionState.Path.IsPSAbsolute($path, $path_drive)
$path_prov = $ExecutionContext.SessionState.Path.IsProviderQualified($path)
# we split the drive away, it makes UnresolvedPath fail on non-existing drives.
$norm_path  = Split-Path $path -NoQualifier
# strip out UNC
$path_direct = $norm_path.StartsWith('//') -or $norm_path.StartsWith('\\')
if ($path_direct) {
    $norm_path = $norm_path.Substring(2)
}
# then normalize
$norm_path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($norm_path)
# then we cut out the current location if same drive
if (($path_drive.Value -eq $pwd.Drive.Name) -or $path_direct) {
    $norm_path = $norm_path.Substring($pwd.Path.Trim('/', '\').Length + 1)
} elseif (-not $path_prov) {
    # or we cut out the current drive
    if ($pwd.Drive) {
        $norm_path = $norm_path.Substring($pwd.Drive.Root.Length)
    } else {
        # or we cut out the UNC special case
        $norm_path = $norm_path.Substring($pwd.ProviderPath.Length + 1)
    }
}
# then add back the UNC if any
if ($path_direct) {
    $norm_path = $pwd.Provider.ItemSeparator + $pwd.Provider.ItemSeparator + $norm_path
}
# then add back the provider if any
if ($path_prov) {
    $norm_path = $ExecutionContext.SessionState.Path.Combine($path_drive.Value + '::/', $norm_path)
}
# or add back the drive if any
elseif ($path_abs) {
    $norm_path = $ExecutionContext.SessionState.Path.Combine($path_drive.Value + ':', $norm_path)
}
$norm_path
pros: doesn't use the dotnet path function, uses proper powershell infrastructure.
cons: kind of complex, depends on `pwd`

 Context cwd
   [+] has no external libraries 8ms (2ms|6ms)
   [+] barely work for FileInfos on existing paths 4ms (3ms|1ms)
   [+] process .. and . (relative paths) 3ms (2ms|1ms)
   [+] must support powershell providers 13ms (13ms|0ms)
   [+] must support powershell drives 3ms (2ms|1ms)
   [+] works with non-existant paths 3ms (2ms|0ms)
   [+] works with non-existant drives 3ms (2ms|1ms)
   [+] barely work for direct UNCs 3ms (2ms|1ms)
 Context reroot
   [+] doesn't reroot subdir 3ms (2ms|1ms)
   [+] doesn't reroot local 3ms (2ms|1ms)
   [+] doesn't reroot parent 15ms (14ms|1ms)
 Context drive root
   [+] works on drive root 4ms (3ms|1ms)
 Context temp drive
   [+] works on temp drive 4ms (3ms|1ms)
   [+] works on temp drive with absolute path 3ms (3ms|1ms)
 Context unc drive
   [+] works on unc drive 9ms (8ms|1ms)
Tests completed in 171ms
Tests Passed: 15, Failed: 0, Skipped: 0 NotRun: 0

I made several other tries, because that's what you do when you are a scientist. So you can pick your poison if that was too complex.
Trust me, you would need to use a stack of paths to do that properly, go read the code of GetUnresolvedProviderPathFromPSPath if you don't believe, and no, you can't do that with regexes because of recursion.

Sources: https://gist.github.com/Luiz-Monad/d5aea290087a89c070da6eec84b33742#file-normalize-path-ps-md

Luiz Felipe
  • 1,123
  • 8
  • 14
0

The expedient parts of the comments here combined such that they unify relative and absolute paths:

[System.IO.Directory]::SetCurrentDirectory($pwd)
[IO.Path]::GetFullPath($dapath)

Some samples:

$fps = '.', 'file.txt', '.\file.txt', '..\file.txt', 'c:\somewhere\file.txt'
$fps | % { [IO.Path]::GetFullPath($_) }

output:

C:\Users\thelonius\tests
C:\Users\thelonius\tests\file.txt
C:\Users\thelonius\tests\file.txt
C:\Users\thelonius\file.txt
c:\somewhere\file.txt
TNT
  • 3,392
  • 1
  • 24
  • 27
  • Ironically this almost works, but changing the process PWD isn't a good idea, it fails on UNC drives. You can see in the test results I made for this https://gist.github.com/Luiz-Monad/d5aea290087a89c070da6eec84b33742#test-set_current_directory – Luiz Felipe Oct 26 '22 at 16:39
0

If you need to get rid of the .. portion, you can use a System.IO.DirectoryInfo object. Use 'fred\frog..\frag' in the constructor. The FullName property will give you the normalized directory name.

The only drawback is that it will give you the entire path (e.g. c:\test\fred\frag).

Dan R
  • 5,258
  • 2
  • 16
  • 10
-1

Well, one way would be:

Join-Path 'fred\frog' '..\frag'.Replace('..', '')

Wait, maybe I misunderstand the question. In your example, is frag a subfolder of frog?

EBGreen
  • 36,735
  • 12
  • 65
  • 85
  • 2
    "is frag a subfolder of frog?" No. The .. means go up one level. frag is a subfolder (or a file) in fred. – dan-gph Jan 31 '09 at 01:48