windowspowershellunix

Powershell to generate file paths correctly in windows and unix


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!


Solution

  • 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:

    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