I want to call a small PowerShell (v5.1) script from Windows 10 Pro command line with arguments.
param($ReqURL, $JSONString)
Write-Host $ReqURL # for debug
Write-Host $JSONString # for debug
Invoke-RestMethod -Uri $ReqURL -Method Patch -ContentType "application/json" -Body $JSONString
Calling the script in a PowerShell console works fine:
.\http-patch.ps1 -ReqURL myRESTApi -JSONString '{"lastname": "Bunny"}'
Calling the script from the Windows Command Prompt (cmd.exe
) works when I escape the double quotes, like this:
PowerShell .\http-patch.ps1 -ReqURL myRESTApi -JSONString '{\"lastname\": \"Bunny\"}'
But when I use the -File
option, it fails because the $JSONString
inside the script is equal to '{"lastname":
:
PowerShell -File "C:\work\LV Demo\http-patch.ps1" -ReqURL myRESTApi -JSONString '{\"lastname\": \"Bunny\"}'
I assume here are some problems with quotes, but I can't find the right way.
CodePudding user response:
When you use the -File
parameter of powershell.exe
, the Windows PowerShell CLI, only double quotes ("
) are recognized as having syntactic function when calling from outside PowerShell, such as from cmd.exe
:
Therefore, switch from '
to "
(the embedded "
chars. require escaping as \"
(sic) either way):
:: From cmd.exe
PowerShell -File "C:\work\LV Demo\http-patch.ps1" -JSONString "{\"lastname\": \"Bunny\"}" -ReqURL myRESTApi
By contrast, when you use -Command
, '...'
strings are recognized, after unescaped "
chars. - the only ones with syntactic function during initial command-line parsing - have been stripped, because the resulting tokens are then interpreted as PowerShell code.
For guidance on when to use -Command
vs. -File
and the differences in resulting parsing, see this answer.
From inside PowerShell, there's rarely a need to to call another PowerShell instance, given that .ps1
files can be invoked directly, in-process.
In cases where you do need to call the CLI from inside PowerShell - say you need to call the CLI of the other PowerShell edition, PowerShell (Core)'s pwsh.exe
, from powershell.exe
or vice versa - the best choice is to use a script block ({ ... }
) (which works only when calling from inside PowerShell), because that:
- allows you to focus solely on PowerShell's own quoting and escaping requirements.
- supports receiving typed output (objects other than strings), behind-the-scenes XML-based serialization, albeit with limited type fidelity - see this answer.
# From PowerShell
# Note how the " chars. now need NO escaping.
# (If you were to use a "..." string, you'd escape them as `" or "")
PowerShell {
& C:\work\LV Demo\http-patch.ps1" -JSONString '{"lastname": "Bunny"}' -ReqURL myRESTApi
}
While you can call via individual arguments, analogous to how you must call from outside PowerShell, you'll not only lose the benefit of typed output, but you'll also run in a longstanding bug up to PowerShell 7.2.x that requires manual escaping of embedded "
chars. as \
when calling external programs - see this answer - as evidenced by one of your own attempts (here, using '...'
is perfectly fine, because PowerShell recognizes it):
# !! Note the unexpected need to \-escape the embedded " chars.
PowerShell -File .\http-patch.ps1 -JSONString '{\"lastname\": \"Bunny\"}' -ReqURL myRESTApi
# Variant with double quotes.
# !! Note the *double* escaping: first with ` - for the sake of PowerShell itself -
# !! then with \ due to the bug.
PowerShell -File .\http-patch.ps1 -JSONString "{\`"lastname\`": \`"Bunny\`"}" -ReqURL myRESTApi