How to normalize a path in PowerShell?

I have two ways:

fred\frog 

and

 ..\frag 

I can combine them together in PowerShell as follows:

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

This gives me the following:

 fred\frog\..\frag 

But I do not want this. I want a normalized path without double points, for example:

 fred\frag 

How can i get this?

+68
powershell path
Jan 30 '09 at 14:15
source share
10 answers

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

Since cd ( Set-Location ) does not change the working directory of the current process, simply passing the relative file name to the .NET API, which the PowerShell context does not understand, can have unintended side effects, such as allowing a path based on the original working directory (rather than at your current location).

What you do is that you first determined your path:

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

This gives (given my current location):

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

With an absolute base, you can safely call the .NET API GetFullPath :

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

Which gives you the full path and with the removal .. :

 C:\WINDOWS\system32\fred\frag 

This is also not difficult, I personally despise solutions that depend on external scripts for this, this is a simple problem that can be solved quite accurately with Join-Path and pwd ( GetFullPath just to make it pretty). If you want to keep only the relative part, you simply add .Substring((pwd).Path.Trim('\').Length + 1) and voila!

 fred\frag 

UPDATE

Thanks to @Dangph for specifying the case of the edge of C:\ .

+53
Dec 12 '12 at 19:39
source share
β€” -

You can expand .. \ frag to its full path using the solution path:

 PS > resolve-path ..\frag 

Try normalizing the path using the comb () method:

 [io.path]::Combine("fred\frog",(resolve-path ..\frag).path) 
+90
Jan 31 '09 at 16:25
source share

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

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

or more interesting

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

both of them give the following (if your current directory is D: \):

 D:\fred\frag 

Note that this method does not attempt to determine if fred or frag really exists.

+19
Jan 30 '09 at 15:22
source share

The accepted answer was a big help, but it doesn’t β€œnormalize” the absolute path correctly. 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; } 
+11
Jun 06 '13 at 14:18
source share

Any access control functions without using PowerShell (for example, in System.IO.Path) will not be reliably protected from PowerShell, because the PowerShell provider model allows the current PowerShell path to differ from what Windows considers the working directory of the process.

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

The following very simple cmdlet should work for non-existent paths. It converts 'fred \ frog \ .. \ frag' to 'd: \ fred \ frag', even if the file or folder 'fred' or 'frag' 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($_) } } } 
+9
Feb 13 2018-11-11T00:
source share

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 } 

Name it as follows:

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

Note that this fragment requires a path to the DLL. There is a trick you can use to search for the folder containing the current executable script, but in my case I had an environment variable that I could use, so I just used this.

+3
Feb 28 '09 at 5:05
source share

Create a function. This function will normalize a path that does not exist on your system, and also will not add drive letters.

 function RemoveDotsInPath { [cmdletbinding()] Param( [Parameter(Position=0, Mandatory=$true)] [string] $PathString = '' ) $newPath = $PathString -creplace '(?<grp>[^\n\\]+\\)+(?<-grp>\.\.\\)+(?(grp)(?!))', '' return $newPath } 

Example:

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

Thanks, Oliver Schadlich, for helping with RegEx.

+2
Jul 20 2018-12-12T00:
source share

This gives the full path:

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

This gives a 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 .

+1
Jan 31 '09 at 1:46
source share

Well, one way:

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

Wait, maybe I misunderstood the question. Is there a fragment of a frog subfolder in your example?

0
Jan 30 '09 at 14:26
source share

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

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

0
Jan 30 '09 at 14:37
source share



All Articles