pwsh -Command is removing quotation marks
Asked Answered
W

3

4

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
Winner answered 25/11, 2019 at 16:51 Comment(1)
May be related issue!Stella
E
19

Update:

  • PowerShell 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:

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+.

Emmett answered 25/11, 2019 at 17:9 Comment(0)
O
1

Another way. Are you in Windows or Unix?

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

{"drop_attr":"name"}
Oceangoing answered 3/1, 2020 at 13:18 Comment(1)
Thanks I am on MacOS, and I got it working in one lineWinner
H
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" }
Heated answered 22/7, 2020 at 5:58 Comment(1)
If you're calling from PowerShell, pwsh -Command { Write-Host '{ "description": "Test program" }' } gives you this ability for free (uses -EncodedCommad behind the scenes).Emmett

© 2022 - 2024 — McMap. All rights reserved.