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-Pathcall isn't an option for joining more than two path components at a time;-ChildPathaccepts only a single string, and the[string[]]-typed-AdditionalChildPathparameter only exists in PowerShell (Core) 7 .A simple workaround is to use the
-joinoperator on the multiple child paths in order to combine them into a single argument (see the next section about using-joininstead ofJoin-Pathand associated considerations):# Windows PowerShell workaround. Join-Path -path 'a' -ChildPath ('b', 'c', 'd' -join '\') # -> 'a/b/c/d'As an aside: By contrast, the
-Pathparameter 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 --ChildPathvalue.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-AdditionalChildPathparameter, which accepts an array of additional components, is defined with theValueFromRemainingArgumentsproperty, 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
-Pathand-ChildPathparameters, 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-Pathuse:- It automatically uses the platform-appropriate path separator (
\vs./) - It prevents duplicate path separators, so that
Join-Path a\ bstill 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'
