JayG30
JayG30

Reputation: 79

Powershell "A positional parameter cannot be found that accepts argument"

I'm getting this below error trying to run this code and I'm not sure how to resolve it. Anyone ideas?

$ChocoPackage = "packagename"
$localprograms = choco list --localonly
Start-Process -Wait -FilePath "C:\ProgramData\chocolatey\choco.exe" -ArgumentList "install $ChocoPackage -ia "'/D=C:\Program Files\packagename'" -y"
Start-Process : A positional parameter cannot be found that accepts argument '/D=C:\Program Files\packagename'.
At C:\Program Files\install.ps1:3 char:5
+     Start-Process -Wait -FilePath "C:\ProgramData\chocolatey\choco.ex ...
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Start-Process], ParameterBindingException
    + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.StartProcessCommand

Upvotes: 1

Views: 17564

Answers (2)

mklement0
mklement0

Reputation: 437052

Note:

  • As you point out in your own answer, special quoting requirements apply to choco.exe's /ia parameter.
  • However, since you are using Start-Process, it is not the instructions from the linked page for PowerShell that apply in this case, but the ones for cmd.exe (even though there is no shell involved in this invocation; however, like cmd.exe, console applications themselves generally only understand "..." quoting); that is, your -ArgumentList string must end up containing the following verbatim, which is what the string passed to -ArgumentList below achieves via embedded " characters escaped as `" ("" would work too):
    "/D=""C:\Program Files\packagename"""
Start-Process -Wait -FilePath "C:\ProgramData\chocolatey\choco.exe" `
  -ArgumentList "install $ChocoPackage -ia `"/D=`"`"C:\Program Files\packagename`"`"`" -y"

Only if you call choco.exe directly from PowerShell do the PowerShell instructions apply ('/D=""C:\Program Files\packagename""'):

& "C:\ProgramData\chocolatey\choco.exe" install $ChocoPackage -ia '/D=""C:\Program Files\packagename""' -y

As for what you tried:

What you passed to -ArgumentList was composed of three directly adjoining string literals in the form " a "' b '" c ", to use a simplified example (i.e., a double-quoted string right next to single-quoted string, right next to another double-quoted string).

However, PowerShell doesn't fully support implicit concatenation of directly adjoining string literals to form a single argument the way that POSIX-compatible shells such as bash do; doing so results in the strings being passed as separate arguments, which caused your problem (Start-Process saw extra positional parameters after -ArgumentList it didn't expect).

To demonstrate the original problem with the simplified example:

PS> Write-Output " a "' b '" c "
 a 
 b 
 c 

That is, Write-Output received string literals " a ", ' b ', and " c " as 3 separate arguments (as implied by their appearing on their own line each).

Only if the first token is unquoted do you get a single string argument composed of it and subsequent quoted tokens:

# *Single* argument, because `a` is *unquoted*.
PS> Write-Output a' b '" c "
a b  c 

The same applies even if that first unquoted token is a (non-expression) variable reference (e.g., Write-Output $HOME/'folder 1'/"and more"; see this answer for more information


If you do need to reliably form a single string argument from a mix of double-quoted (interpolating) and single-quoted (verbatim) string literals, use (...), the grouping operator, and the + operator for explicit string concatenation:

PS> Write-Output (" a " + ' b ' + " c ")
 a  b  c 

Upvotes: 2

JayG30
JayG30

Reputation: 79

Oh course I figured it out almost immediately after reading the official Chocolatey documentation. Since I'm using powershell you have to be very specific in how you wrap spaces in quotes.

-ia '/yo=""Spaces spaces""'

https://docs.chocolatey.org/en-us/choco/commands/#how-to-pass-options-switches

Pass quotes in arguments: When you need to pass quoted values to to something like a native installer, you are in for a world of fun. In cmd.exe you must pass it like this: -ia "/yo=""Spaces spaces""". In PowerShell.exe, you must pass it like this: -ia '/yo=""Spaces spaces""'. No other combination will work. In PowerShell.exe if you are on version v3+, you can try --% before -ia to just pass the args through as is, which means it should not require any special workarounds.

Upvotes: 0

Related Questions