How to pass JSON (string data) to PowerShell?

匆匆过客 提交于 2019-12-08 07:04:53

问题


I'm passing the following as an argument to powershell (v4 on w7):

-debugWrite -fileName SettingsFile -jsonContent { "c": "some setting", "d":  "unknown", "b": "some thing", "a": 1 }

But PS gets hung up on the JSON. I've tried delimiting the \double-quotes\ and putting everything after -jsonContent in 'single quotes', but to no avail.

Here is the Windows 7 (PS4) environment PS is running in:

note: "..." obfuscation refers to the same dir. IOW, all files live in the same directory.

A batch file is run, kicking off the whole thing:

    "C:\...\script.bat" > "C:\...\script-output.txt" 2>&1

This runs script.bat and outputs to script-output.txt. script.bat is a long 1-liner:

%WINDIR%\sysnative\windowspowershell\v1.0\powershell.exe -ExecutionPolicy Bypass -File "C:\...\customscript.PS1" --% -fileName DropFileToCreate -jsonContent "{     "c":  "some setting",     "d":  "unknown",     "b":  "some thing",     "a":  1 }" -debugWrite

Legend:

DropFileToCreate - the filename passed to the PS script, used to create a file in same dir.

-jsonContent - A named parameter in the script (see below for header of customscript.PS1)

In the above example, the JSON is:

"{ "c": "some setting", "d": "unknown", "b": "some thing", "a": 1 }"

-debugWrite - A switch parameter (used here to enable Write-Host debugging)

Finally, a bit of the customscript.PS1:

Param (
    [Parameter(Mandatory = $True)]
    [String]
    $fileName,
    [Parameter(Mandatory = $True)]
    $jsonContent,
    [Parameter(Mandatory = $False)]
    [Switch]
    $debugWrite = $False
)
[...]

The JSON is easier seen, and spaces explained, if expressed as:

{
 "c": "some setting",
 "d": "unknown",
 "b": "some thing",
 "a": 1 
}

回答1:


tl;dr

Your overall "..."-enclosed JSON string has embedded ", which must be escaped as \" (sic; command simplified):

powershell.exe -File "C:\...\customscript.PS1" ... -jsonContent "{ \"c\": \"some setting\", \"d\": \"unknown\", \"b\": \"some thing\", \"a\": 1 }"

Read on for when additional escaping is needed, how -File invocation differs from -Command invocation, and in what way the calling shell (where you call powershell.exe from) matters.


Note:

  • This answer primarily discusses use of the Windows PowerShell executable, powershell.exe, but it applies analogously to the PowerShell Core executable, pwsh, and there's a section on calling from bash at the bottom.

  • The section Calling from PowerShell itself below, specifically the syntax required with -File, applies to passing JSON to other programs such as curl.exe as well.

The required syntax for the PowerShell CLI - that is, invoking powershell.exe with arguments - depends on:

  • whether you're calling from cmd.exe (Command Prompt / batch file) or from PowerShell itself (or, in PowerShell Core from a POSIX-like shell such as bash).

  • whether you pass arguments to powershell -Command (inline command) or
    powerShell -File (script path).

Either way, your original attempt could not have worked, because literal { "c": "some setting" ... } cannot be recognized as a single argument, due to containing whitespace and not being enclosed in quotes overall; the command added later, with enclosing "...", lacks escaping of the embedded ".

The following commands demonstrate the required syntax for the scenarios discussed, using a simplified JSON string.

To make the -File commands runnable, create a script.ps1 file in the current dir. with the following content: ConvertFrom-Json $Args[0]


Calling from cmd.exe / a batch file

  • Embedded " must be escaped as \" (even though PowerShell-internally you'd use `").

  • Important:

    • If the JSON text contains cmd.exe metacharacters (invariably between \"...\" runs), you must ^-escape them individually, because cmd.exe, due to not recognizing \" as an escaped ", considers these substrings unquoted; e.g., \"some & setting\" must be escaped as \"some ^& setting\"; the cmd.exe metacharacters that need escaping here are:
      & | < > ^

    • cmd.exe-style environment-variable references such as %USERNAME% are interpolated - cmd.exe has no literal string syntax, it only recognizes "...", where interpolation does take place, just as in unquoted tokens.
      If you want to pass such a token as-is, i.e., to suppress interpolation, the escaping syntax depends on whether you're calling from the command line or a batch file, sadly: use %^USERNAME% from the former, and %%USERNAME%% from the latter - see this answer for the gory details.

  • Note how the -Command calls simply add another layer of quoting, by enclosing the "..." string in '...'. This is required, because with -Command PowerShell treats the arguments it receives as PowerShell source code rather than as literal arguments (the latter is what happens with -File); if it weren't for the enclosing '...', the overall enclosing "..." would be stripped before interpretation.

With -File:

# With a literal string:
powershell -File ./script.ps1 "{ \"c\": \"some setting\", \"unknown\": \"b\" }"

# With an expandable string (expanded by the caller):
powershell -File ./script.ps1 "{ \"c\": \"some %USERNAME%\", \"unknown\": \"b\" }"

With -Command:

# With a literal string:
powershell -Command ConvertFrom-Json '"{ \"c\": \"some setting\", \"unknown\": \"b\" }"'

# With an expandable string (expanded by the caller):
powershell -Command ConvertFrom-Json '"{ \"c\": \"some %USERNAME%\", \"unknown\": \"b\" }"'

Calling from PowerShell itself

  • Calling from PowerShell makes the need to escape cmd.exe metacharacters go away, because cmd.exe is not involved.

  • PowerShell's string-quoting rules apply, which simplifies matters, although, sadly, you still need to manually \-escape embedded " chars.; see this GitHub issue for background.

    • Using outer '...' quoting simplifies the syntax for the embedded quoting, but that limits you to passing literal strings.

    • Using outer "..." allows you to embed variable references and expressions from the caller (which are expanded by the caller, before the argument is passed), but it complicates the syntax, given that an embedded " must then be doubly escaped as \`" (sic): first with ` to conform to PowerShell-internal syntax, then with \ to satisfy the PowerShell CLI's requirements.

    • If your JSON text is not a literal and stored in a variable, you must pass
      $jsonVar -replace '"', '\"' to perform the necessary escaping - see this answer.

With -File or when calling external programs such as curl.exe:

# With a literal string:
powershell -File ./script.ps1 '{ \"c\": \"some setting\", \"unknown\": \"b\" }'

# With an expandable string (expanded by the caller):
powershell -File ./script.ps1 "{ \`"c\`": \`"some $env:OS\`", \`"unknown\`": \`"b\`" }"

With -Command:

# With a literal string:
powershell -Command ConvertFrom-Json '''"{ \"c\": \"some setting\", \"unknown\": \"b\" }"'''

# With an expandable string (expanded by the caller):
powershell -Command ConvertFrom-Json "'{ \`"c\`": \`"some $env:OS\`", \`"unknown\`": \`"b\`" }'"

PowerShell Core: Calling from bash

  • Bash, like PowerShell, understands both expanding (interpolating) "..." strings and literal '...' strings.

  • Bash, unlike cmd.exe, recognizes \" as escaped " chars. inside "...", so there's no need to escape any of Bash's metacharacters.

With -File:

# With a literal string:
pwsh -File ./script.ps1 '{ "c": "some setting", "unknown": "b" }'

# With an expandable string (expanded by the caller):
pwsh -File ./script.ps1 "{ \"c\": \"some $USER\", \"unknown\": \"b\" }"

With -Command:

# With a literal string:
pwsh -Command ConvertFrom-Json \''{ "c": "some setting", "unknown": "b" }'\'

# With an expandable string (expanded by the caller):
pwsh -Command ConvertFrom-Json "'{ \"c\": \"some $USER\", \"unknown\": \"b\" }'"


来源:https://stackoverflow.com/questions/53490414/how-to-pass-json-string-data-to-powershell

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!