4

I want to generate a string for a file path inside a powershell script. I want this to work in both in windows and mac.

At the moment the code is hardcoded to Windows-like paths (\ -> Windows, / -> Unix):

$templatep="$CoreRoot\templates\$serviceName"

I changed this to:

$templatep= Join-Path $CoreRoot "templates" $serviceName

And it works in mac with Powershell 6.0. BUT it doesn't work in my Windows server with Powershell 4. I have to do something like this:

$templatep= Join-Path $CoreRoot -ChildPath "templates" | Join-Path -ChildPath $serviceName

Any idea why this is just working in my mac? Is this a new feature in powershell 5 or 6? I don't like the having to pipe multiple Join-Paths. Is there a better way to do this?

Thanks!

1
  • 2
    Through PowerShell 5.0, to my knowledge Join-Path can only take two paths. Piping to another join or using .Net's [io.path]::combine() method are the two workarounds I know of. Commented Jan 20, 2017 at 17:15

1 Answer 1

10

First, a workaround using the .NET framework:

[IO.Path]::Combine('a', 'b', 'c')

This yields a/b/c on Unix, and a\b\c on Windows, and conveniently supports any number of path components.

Note:

  • This workaround is only for filesystem paths, whereas Join-Path is designed to work with any PowerShell drive provider's paths.

  • Make sure that no component other than the first starts with \ (Windows) or / (Unix), because any preceding component is then ignored; e.g., on Windows:
    [IO.Path]::Combine('\a', '\b', 'c') # -> '\b\c' - '\a' is ignored(!)
    Note that Join-Path does not exhibit this behavior; see this answer for details.

As an alternative to sequencing Join-Path calls with a pipeline you can simply use (...) (a subexpression):

Join-Path a (Join-Path b c)  # -> 'a\b\c' (on Windows)

The syntax displayed by Join-Path -? as of Windows PowerShell v5.1.14393.693 (incidental parameters omitted):

Join-Path [-Path] <String[]> [-ChildPath] <String> ...

This syntax implies that invocation Join-Path a b c results in a syntax error in Windows PowerShell, because there is no parameter to bind the c argument to.

By contrast, the syntax displayed in PowerShell (Core) 7 reveals an additional parameter:

Join-Path [-Path] <String[]> [-ChildPath] <String> [[-AdditionalChildPath] <String[]>]

It is the additional -AdditionalChildPath parameter, which is declared in a manner that collects all remaining positional arguments that (ValueFromRemainingArguments), that makes specifying an arbitrary number of child components work, so that Join-Path a b c indeed works, for instance.

Unfortunately, this enhancement won't be back-ported to Windows PowerShell.

Note that even though [-Path] <String[]> is an array parameter, its purpose is not to accept multiple child path components of a single output path, but to allow joining of multiple parent-child path pairs; e.g.:

$ Join-Path a,b c  # same as: Join-Path -Path a,b -ChildPath c
a\c
b\c

As of PowerShell 7.4.x, there is a green-lit, but as yet unimplemented feature request to allow passing an array of components directly to -ChildPath, without the need for -AdditionalPath:

# FUTURE ENHANCEMENT, in *some* version *after* 7.4
Join-Path -Path a -ChildPath b, c
# Ditto with positional arguments
Join-Path a  b, c

See GitHub issue #21367


Finally, even you can typically get away with hard-coding / as the path separator on both platforms, because many Windows API functions as well as PowerShell's own cmdlets accept \ and / interchangeably.
However, not all utilities may behave this way, so it's generally safer to use the platform-appropriate separator.

For instance, the following works just fine on Windows:

Get-Item c:/windows/system32 # same as: Get-Item c:\windows\system32
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.