3

Why can't we have a -Path C:/some/path inside a string parameter?

Using -Command:

pwsh.exe -Command .\hello.ps1 -A 123 -B "-Path a/b/c"

hello.ps1: A positional parameter cannot be found that accepts argument 'a/b/c'.

pwsh.exe -Command .\hello.ps1 -A 123 -B '-Path a/b/c'

hello.ps1: A positional parameter cannot be found that accepts argument 'a/b/c'.

Using -File:

pwsh.exe -File .\hello.ps1 -A 123 -B "-Path a/b/c"

Hello!
B: -Path a/b/c

Good! Now with a rooted path:

pwsh.exe -File .\hello.ps1 -A 123 -B "-Path C:/a/b/c"

hello.ps1: Missing an argument for parameter 'B'. Specify a parameter of type 'System.String' and try again.

Hmm.. now with a unix style path:

pwsh.exe -File .\hello.ps1 -A 123 -B "-Path /c/a/b/c"

Hello!
B: -Path /c/a/b/c

ok... but what's wrong with the colon?

pwsh.exe -File .\hello.ps1 -A 123 -B "-Path :"

hello.ps1: Missing an argument for parameter 'B'. Specify a parameter of type 'System.String' and try again.

pwsh.exe -File .\hello.ps1 -A 123 -B ":"

Hello!
B: :

So it's really when the string has -Something before the colon that the issue arise. Is there a way to escape that colon?

(PSVersion 7.2.0)

[EDIT] hello.ps1 would be a run script that spawns a subprocess (could be any executable, including pwsh.exe)

[EDIT (based on answers)]

Possible wordarounds:

  • Quotes: pwsh.exe -Command "./hello.ps1 -A 123 -B '-Path C:/a/b/c'"
  • Script block and quotes: pwsh.exe -Command "& {./hello.ps1 -A 123 -B '-Path C:/a/b/c'}"
CandyRedTele
  • 33
  • 1
  • 5
  • 1
    Read and follow `pwsh.exe -?`. Use `pwsh.exe -Command "& {%CD%\hello.ps1 -A 123 -B ""-Path a/b/c""}"`. – JosefZ Nov 10 '21 at 16:38
  • 2
    Does this answer your question? [How do I call the PowerShell CLI robustly, with respect to character encoding, input and output streams, quoting and escaping?](https://stackoverflow.com/questions/68136128/how-do-i-call-the-powershell-cli-robustly-with-respect-to-character-encoding-i) – zett42 Nov 10 '21 at 16:54
  • 1
    @zett42, that's an excellent post to link to :) - but we're dealing with a bona fide _bug_ here. – mklement0 Nov 10 '21 at 20:37
  • This works indeed and could be a workaround (using single quotes): `pwsh.exe -Command "& {./hello.ps1 -A 123 -B '-Path C:/a/b/c'}"` – CandyRedTele Nov 10 '21 at 20:42
  • 1
    @CandyRedTele, from _inside_ PowerShell, just use `{ ... }` - unquoted - as shown at the top of my answer. From _outside_ PowerShell, use `"./hello.ps1 -A 123 -B '-Path C:/a/b/c'"` (as shown later in my answer) - no need for `"& { ... }"` – mklement0 Nov 10 '21 at 20:52

1 Answers1

2

Note: If you're calling from within PowerShell and really want to create another PowerShell process (which is unusual), the best solution, which bypasses all quoting headaches, is to pass a script block ({ ... }) to the (positionally implied) -Command paramter:

# Works robustly; if you need to include variable values from the 
# current session:
#  * append -Args ... 
#  * and refer to them via $args inside the script block.
pwsh.exe { .\hello.ps1 -A 123 -B "-Path :" }

The following pertains to calls to pwsh, the PowerShell (Core) CLI, from the outside:


pwsh.exe -Command .\hello.ps1 -A 123 -B "-Path a/b/c"

That this fails is to be expected: Using -Command means that after syntactic " have been stripped during command-line parsing the result is then interpreted as PowerShell code, which means that your call is equivalent to the following in-session call:

# !! Fails, because, after " stripping, *two* arguments are passed to `-B`
.\hello.ps1 -A 123 -B -Path a/b/c

The problem does not arise with '...' quoting (your 2nd command) when called from cmd.exe, because ' has no syntactic function on the command line when PowerShell is called from the outside, and is therefore passed through.

However, the caveat is that - since cmd.exe doesn't (ever) recognize '...' quoting - any cmd.exe metacharacters such as & would have to be ^-escaped. You can avoid that if you enclose the entire PowerShell command in "...", however (that would also make it work from PowerShell, but, as stated, use of script blocks is preferable there):

# OK - overall "..." quoting, which protects the value inside '...'
pwsh.exe -Command ".\hello.ps1 -A 123 -B '-Path a/b/c&d'"

By contrast, using -File does not entail parsing the command-line arguments that follow as a piece of PowerShell code, and instead:

  • (always) requires distinct arguments: the script-file path followed by the arguments to pass to it.
  • requires nothing but syntactic quoting with "...", as needed, and treats the arguments as literals.

Therefore,

pwsh.exe -File .\hello.ps1 -A 123 -B "-Path :"

should work - passing verbatim -Path : as the value to parameter -B - but doesn't, due to a bug, still present as of PowerShell 7.2.

The problem is the combination of an argument-initial - with a subsequent :, which - inappropriately - causes PowerShell to consider the argument a named argument, i.e. a parameter name-value combination; that is, the -Path part is interpreted as targeting a parameter by that name (with whatever follows : as the parameter value), leaving parameter -B without an argument, which causes the error you saw.

There is no workaround per se, only a suboptimal one that requires extra work on the part of the script too: you can start your argument with a space and later trim it inside .\hello.ps1:

pwsh.exe -File .\hello.ps1 -A 123 -B " -Path :"
mklement0
  • 382,024
  • 64
  • 607
  • 775