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'}"
CodePudding user response:
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 has been reported in GitHub issue #16432
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 :"