I want to create folders like this with variables in the path, I tried join-path and it tells me that "System.Object[]" cannot be converted to type "System.String" required by parameter "ChildPath". The specified method is not supported.
$variables\A\$variables
$variables\I
$variables\L\Cs\cz
$variables\L\Da\dk
$variables\L\Nl\nl
$variables\L\En\uk
$variables\L\En\us
$variables\M
$variables\U\Ed
I'm just new to powershell and this is my script:
$variables = Read-Host -Prompt 'Type here'
$dir = Join-Path -path $PSScriptRoot\$variables -ChildPath "A","I","L","M","U"
#-AdditionalChildPath I don't know how to create more ChildPath
mkdir $dir
CodePudding user response:
The -ChildPath
parameter accepts only a single string, so you have to call Join-Path
multiple times, if you'd like to individually join multiple child paths to a base path, creating an array of paths (if that is what you want):
$dir = foreach( $childPath in "A","I","L","M","U" ) {
Join-Path -Path $PSScriptRoot\$variables -ChildPath $childPath
}
This captures all (implicit) output from the foreach
loop body in variable $dir
, automatically creating an array like this:
c:\foo\A
c:\foo\I
c:\foo\L
c:\foo\M
c:\foo\U
Alternatively you may take advantage of the fact that the -ChildPath
parameter accepts pipeline input (as documented):
$dir = "A","I","L","M","U" | Join-Path -Path $PSScriptRoot\$variables -ChildPath { $_ }
The -ChildPath
argument is a delay-bind script block that simply passes the current pipeline object to the Join-Path
command. As in the foreach
loop, Join-Path
gets called for each of the letters that are passed as input to the pipeline. Again, all output is captured in $dir
as an array.
In a comment you mention -AdditionalChildPath
. This parameter actually accepts an array, but the -ChildPath
parameter is still mandatory and must be specified as well:
Join-Path -Path A -ChildPath B -AdditionalChildPath C, D
Output is a single path:
A\B\C\D
This call syntax is somewhat inconvenient if you'd like to join an arbitrary number of child paths, defined as an array. For that you may use array splatting:
$childPaths = 'B','C','D'
Join-Path -Path A @childPaths
Output:
A\B\C\D
CodePudding user response:
To add to zett42's helpful answer:
In Windows PowerShell, use of a single
Join-Path
call isn't an option for joining more than two path components at a time;-ChildPath
accepts only a single string, and the[string[]]
-typed-AdditionalChildPath
parameter only exists in PowerShell (Core) 7 .A simple workaround is to use the
-join
operator on the multiple child paths in order to combine them into a single argument (see the next section about using-join
instead ofJoin-Path
and associated considerations):# Windows PowerShell workaround. Join-Path -path 'a' -ChildPath ('b', 'c', 'd' -join '\') # -> 'a/b/c/d'
As an aside: By contrast, the
-Path
parameter is[string[]]
-typed (i.e., it accepts an array of string values), which means that each among multiple values passed to it is combined with the - one and only --ChildPath
value.Join-Path -path 'a', 'b' -ChildPath 'z' # -> 'a\z', 'b\z'
In PowerShell (Core) 7 , you can join an open-ended number of components, and the most convenient syntax is to pass all path components as individual, positional arguments, because the
[string[]
-typed-AdditionalChildPath
parameter, which accepts an array of additional components, is defined with theValueFromRemainingArguments
property, meaning that it collects any (remaining) positional arguments (i.e., those not preceded by the target parameter name, such as-ChildPath
).Together with positionally binding the
-Path
and-ChildPath
parameters, you can simply pass all components positionally, optionally in an array via splatting; e.g.:# PS v7 only # Individual arguments: # 'a' binds positionally to -Path # 'b' binds positionally to -ChildPath # 'c' and 'd' bind to -AdditionalChildPath via ValueFromRemainingArguments Join-Path a b c d # -> 'a\b\c\d' # Ditto, via array splatting. # You may combine splatting with passing the first (-Path) argument # individually, as in zett42's answer, or also the second one (`-ChildPath`) $allComponents = 'a', 'b', 'c', 'd' Join-Path @allComponents # -> 'a\b\c\d'
Alternatives to Join-Path
:
Unless you need to resolve wildcard-based paths to matching literal paths with the -Resolve
switch, you don't strictly need Join-Path
, and simply using the -join
operator on an array may be sufficient:
$allComponents = 'a', 'b', 'c', 'd'
$allComponents -join '\' # -> 'a\b\c\d'
# For cross-platform use (if necessary):
$allComponents -join [IO.Path]::DirectorySeparatorChar # -> 'a\b\c\d' or 'a/b/c/d'
Note:
PowerShell itself accepts
\
and/
interchangeably as path separators, so even on Unix-like platforms you can get away with using\
, as long as it is only PowerShell cmdlets that interpret the resulting paths.Arguments for
Join-Path
use:- It automatically uses the platform-appropriate path separator (
\
vs./
) - It prevents duplicate path separators, so that
Join-Path a\ b
still yields'a\b'
on Windows, for instance.
However, note that duplicate path separators (e.g.a\\b
) are generally not a problem in that paths with them are still recognized properly.
- It automatically uses the platform-appropriate path separator (
For the sake of completeness: You may also use the [System.IO.Path]::Combine()
method, which is cross-platform aware; however, there are pitfalls:
You must cast a regular PowerShell array (which is
[object[]
-typed) to[string[]]
in order for PowerShell to find the desired method overload:$allComponents = 'a', 'b', 'c', 'd' # Note: [string[]] cast only required in Windows PowerShell. [IO.Path]::Combine([string[]] $allComponents) # -> 'a\b\c\d' or 'a/b/c/d' # However, you may pass *up to 4* arguments *individually*: [IO.Path]::Combine('a', 'b', 'c', 'd')
If a component other than the first one starts with a (platform-appropriate) path separator, the preceding components are ignored; e.g.:
# Windows example [IO.Path]::Combine('a', 'b', '\c', 'd') # -> !! '\c\d'