7

In pwsh call the following:

Write-Host '{"drop_attr": "name"}' 

Result ok:

{"drop_attr": "name"}

Now do the same via pwsh:

pwsh -Command Write-Host '{"drop_attr": "name"}'

Result is missing quotation marks and square brackets?

drop_attr: name
1
  • 1
    May be related issue! Commented Nov 25, 2019 at 16:54

3 Answers 3

22

Update:

  • PowerShell (Core) 7.3.0 mostly fixed the problem, with selective exceptions on Windows - see this answer for details.

  • For cross-version, cross-edition code, the Native module discussed at the bottom may still be of interest.


Unfortunately, PowerShell's handling of passing arguments with embedded " chars. to external programs - which includes PowerShell's own CLI (pwsh) - is fundamentally broken (and always has been), up to at least PowerShell 7.2.x, as well as in Windows PowerShell (the legacy, ships-with-Windows, Windows-only edition of PowerShell whose latest and last version is 5.1)

You need to manually \-escape " instances embedded in your arguments in order for them to be correctly passed through to external programs (which happens to be PowerShell in this case as well):

# Note: The embedded '' sequences are the normal and expected
#       way to escape ' chars. inside a PowerShell '...' string.
#       What is *unexpected* is the need to escape " as \"
#       even though " can normally be used *as-is* inside a '...' string.
pwsh -Command ' ''{\"drop_attr\": \"name\"}'' '

Note that I'm assuming your intent is to pass a JSON string, hence the inner '' ... '' quoting (escaped single quotes), which ensures that pwsh ultimately sees a single-quoted string ('...'). (No need for an explicit output command; PowerShell implicitly prints command and expression output).

Another way to demonstrate this on Windows is via the standard choice.exe utility, repurposed to simply print its /m (message) argument (followed by verbatim [Y,N]?Y):

# This *should* preserve the ", but doesn't as of v7.2
PS> choice /d Y /t 0 /m '{"drop_attr": "name"}'
{drop_attr: name} [Y,N]?Y      # !! " were REMOVED

# Only the extra \-escaping preserves the "
PS> choice /d Y /t 0 /m '{\"drop_attr\": \"name\"}'
{"drop_attr": "name"} [Y,N]?Y  # OK

Note that from inside PowerShell, you can avoid the need for \-escaping, if you call pwsh with a script block ({ ... }) - but that only works when calling PowerShell itself, not other external programs:

# NOTE: Works from PowerShell only.
pwsh -Command { '{"drop_attr": "name"}' }

Background info on PowerShell's broken handling of arguments with embedded " in external-program calls, as of PowerShell 7.2.1:

  • This GitHub docs issue contains background information.

  • GitHub issue #1995 discusses the problem and the details of the broken behavior as well as manual workarounds are summarized in this comment; the state of the discussion as of PowerShell (Core) 7 seems to be:

    • [SEE UPDATE AT THE TOP] A fix is being considered as an experimental feature, which may become an official feature, in v7.3 at the earliest. Whether it will become a regular feature - i.e whether the default behavior will be fixed or whether the fix will require opt-in or even if the feature will become official at all - remains to be seen.

      • Fixing the default behavior would substantially break backward compatibility; as of this writing, this has never been allowed, but a discussion as to whether to allow breaking changes in the future and how to manage them has begun: see GitHub issue #13129.
    • See GitHub PR #14692 for the relevant experimental feature, which, however, as of this writing is missing vital accommodations for batch files and msiexec-style executables on Windows - see GitHub issue #15143.

  • In the meantime, you can use the PSv3+ ie helper function from the Native module (in PSv5+, install with Install-Module Native from the PowerShell Gallery), which internally compensates for all broken behavior and allows passing arguments as expected; e.g.,
    ie pwsh -Command ' ''{"drop_attr": "name"}'' ' would then work properly.
    ie is a cross-edition, forward-compatible solution (i.e. it also work in PowerShell (Core) 7.3+), and also compensates for the behaviors with certain types of CLIs that were not fixed in v7.3+.

Sign up to request clarification or add additional context in comments.

Comments

1

Another way. Are you in Windows or Unix?

pwsh -c "[pscustomobject]@{drop_attr='name'} | convertto-json -compress"

{"drop_attr":"name"}

1 Comment

Thanks I am on MacOS, and I got it working in one line
0

Another way is to use "encoded commands".

> $cmd1 = "Write-Host '{ ""description"": ""Test program"" }'"
> pwsh -encoded ([Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($cmd1)))
{ "description": "Test program" }

1 Comment

If you're calling from PowerShell, pwsh -Command { Write-Host '{ "description": "Test program" }' } gives you this ability for free (uses -EncodedCommad behind the scenes).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.