To complement Martin Brandl with a useful answer :
In particular, the logic - counter-intuitive - applied by [System.IO.Path]::Combine : "If path2 or path3 is also an absolute path, the join operation cancels all previously merged paths and resets to this absolute path." (this is also similar to other overload methods).
In other words: the last path argument specified, starting with \ (including \\ ) or <letter>: (disk specification), ignores all previous paths.
> [IO.Path]::Combine('\foo', '\bar', '\baz') \baz
For the expected behavior — where the start \ should be considered an optional part of each component of the path other than the first — make sure that every component except the first does not start with the \ - Join-Path handle which, as you would expect.
A simple function can do this:
# Combines array of path components $a to a single path. function combine([string[]] $a) { [IO.Path]::Combine([string[]] (@($a[0]) + $a[1..$($a.count-1)] -replace '^\\', '')) }
Please note that for this to work, it is important to use [string[]] .
Call example:
> combine '\foo', '\bar', '\baz' \foo\bar\baz
Using Join-Path , which only supports pairs of components , like Windows PowerShell v5.1 [1] , an alternative to using the pipeline for chaining multiple calls is to expand multiple calls using (...) :
> Join-Path \foo (Join-Path \bar \baz) \foo\bar\baz
Note that although nesting is somewhat inconvenient, you do not need to worry about subordinate components starting with \ .
As for the PowerShell logic of the native Join-Path cmdlet :
Although it is clear that the -ChildPath parameter supports an array of paths (which it does not), it is important to understand that the design of Join-Path fundamentally different from the design of [System.IO.Path]::Combine() , with Windows PowerShell v5.1 [1 ]
Join-Path uses multiple input paths to generate multiple output paths instead of interpreting input paths as components of a single output path.
Input paths are pairs of parent and child paths , with each pair connecting and leading to its own output path:
> Join-Path \foo \bar \foo\bar- Shortcut for:
Join-Path -Path \foo -ChildPath \bar - Note that PS, unlike
[System.IO.Path]::Combine() , processes the optional leading \ in the child path, as you would expect.
While the parent path parameter ( -Path ) supports an array of parent paths, the child path parameter ( -ChildPath ) does not work, although when combined with the -Resolve parameter and wildcard characters, it can effectively result in multiple child paths.
> Join-Path -Path \foo, \baz -ChildPath \bar \foo\bar \baz\bar- Notice how 2 output paths were created by pairing each parent path with one child path.
Unlike [System.IO.Path]::Combine() Join-Path does not necessarily support splitting wildcard paths with -Resolve :
> Join-Path -Resolve C:\Win* Sys* C:\Windows\System C:\Windows\System32 C:\Windows\SystemApps C:\Windows\SystemResources C:\Windows\SysWOW64 C:\Windows\system.ini
Finally, it is worth noting that Join-Path works not only with file paths, but also with the paths of any PowerShell provider [hierarchical data warehouse], using the appropriate provider path separators.
[1] a cross-platform version of PowerShell, PowerShell Core , starting with version 6.0.0, already supports an arbitrary number of child components , to enable calls such as Join-Path abc to get a\b\c (Windows) or a/b/c (Unix) , while Windows Windows PowerShell with v5.1 still does not work, t support it.
This new behavior has not yet been documented (and not even reflected in the syntax in Join-Path -? ), But it may eventually end up in Windows PowerShell (and therefore all releases).