1

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 properly create more childpaths,they are created at the wrong path
mkdir $dir

edit:

Considering that the L folder has many subfolders, so I tried foreach nested, which created the folders correctly, but at the same time returned a lot of An item with the specified name...already exists.

$var = Read-Host -Prompt 'Type here'
$loc = "Cs\cz","Da\dk","Nl\nl","En\uk","En\us"
$dir = foreach ($childPath in "A\$var","I","L","M","U\Ed") {
    Join-Path $PSScriptRoot\$var $childPath
    foreach ($additionalChildPath in $loc) {
        Join-Path $PSScriptRoot\$var\L $additionalChildPath    
    }
}
mkdir $dir

2 Answers 2

1

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
Sign up to request clarification or add additional context in comments.

1 Comment

The last one seems to work, thank you zett!
0

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 of Join-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 the ValueFromRemainingArguments 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:

You don't strictly need Join-Path, because string operations may do in many cases. However, Join-Path is still worth considering for the following reasons:

  • It allows you to resolve wildcard-based paths to matching literal paths with the -Resolve switch.

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

If the path components are available as individual variables or strings, you can simply use an expandable (double-quoted) string ("...") to synthesize your path:

"$PSScriptRoot\$variable\A\I\L\M\U"

Note: The above hard-codes the path separator as \ - see below for cross-platform considerations.

If the path components are available as elements of an array use 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.

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'
    

5 Comments

As @zett42 said, -ChildPath only accepts a single string, some examples create the same subfolder in each folder, which is obviously not what I expect, the array only creates a single path, does this mean I have to repeat it?
This seems easy to do in cmd, is there a concise way in powershell? md %a%\b\c d\e\f
@user19418817, yes, there is - please see my update. In short: use an expandable string: "$a\b\c d\e\f"
I'm not a coder and never worked with loops or anything, I edited my question, do you have a better suggestion for this?
@user19418817, I'm unclear on the details of what you're trying to achieve, and I suggest you create a new question post with the specific problem you're now facing, along with the expected output. The error message comes from $dir containing the same directory multiple times. A quick fix would be to add -Force to your mkdir command, which doesn't report an error if a target dir. already exists.

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.