1

This question was asked many times on SO and yet...
All I've seen were solutions where the input string has to be modified. Either by replacing all double quotes with single quotes or by using backticks.

But I have no control over the input string since I have no access to the source. I cannot change Hello "W"orld to Hello 'W'orld or Hello """W"""orld

What I can do is to wrap the whole string with any escaping characters. For example with single quotes around 'Hello "W"orld'. But none of thoses escaping mechanisms I tried worked. And I can change my PowerShell script

Q: How can I pass a string with double quotes to PowerShell as argument and retain the quotes?

How to reproduce

  1. Save this

    cls
    write-host $args[0]
    

    as PowerShell script echoArgs1.ps1 on your desktop.

  2. Open a CMD window, navigate to your desktop folder and enter

    powershell -file echoArgs1.ps1 "Hello "W"orld" 
    

Current Output

enter image description here

Desired Output

enter image description here

6
  • 2
    How exactly is the input string containing the quotes obtained and passed to powershell? Commented Aug 17, 2019 at 12:33
  • powershell -file echoArgs1.ps1 "Hello """W"""orld" should work (note tripled inner "s). Commented Aug 17, 2019 at 12:47
  • 1
    @JosefZ I explicitely said I have no control over the input string Commented Aug 17, 2019 at 12:59
  • 1
    @MathiasR.Jessen The input string comes via Visual Studio and the integrated External Tools Utility. Under Command I Put C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe and under Arguments I put -file "D:\echoArgs.ps1" "$(CurText)": Then i select any text in Visual Studio and execute that command. The crucial part is probably "$(CurText)" Commented Aug 17, 2019 at 13:08
  • 1
    @nixda: That you're invoking PowerShell via a Visual Studio external-tool definition is important information that should be part of the question itself - please update it accordingly. Commented Aug 18, 2019 at 23:13

2 Answers 2

1

You're using the $(CurText) macro to pass the currently selected text in Visual Studio to a PowerShell script file via an external tools definition.

Unfortunately, Visual Studio doesn't offer a way to escape double quotes in the selected text to guarantee that it is ultimately seen as-is by whatever external executable you pass it to.

(For most executables, including PowerShell, embedding literal " chars. in a "..."-enclosed argument requires escaping them as \" - see this answer for the full story.)

Due to this lack of proper escaping, PowerShell won't parse text passed as an argument to a script file (*.ps1) via the -File CLI parameter as expected if it contains literal " chars.

This is Visual Studio's shortcoming, but there is a workaround:

With just one argument being passed, inspect the raw command line via [Environment]::CommandLine, and consider everything after the *.ps1 file the argument, verbatim.

To simplify that process, pass $(CurText) without enclosing it in "..." in the external-tool definition (and make sure that it is separated from the previous token by just one space char.).

Inside of echoArgs1.ps1, use the following command to retrieve the argument verbatim:

$rawText = ([Environment]::CommandLine -split '\.ps1 ', 2)[-1]
Sign up to request clarification or add additional context in comments.

Comments

0

The problem is that the command line interpreter has already removed the quotes. In other words, the quotes are already gone before the command reaches the PowerShell interpreter.

What you might try to do is: pulling the original bare command line ($MyInvocation.Line) and resolve the arguments by removing the command itself:

$FileName = [System.IO.Path]::GetFileName($MyInvocation.MyCommand.Path)
$Arguments = $MyInvocation.Line  -Replace ("^.*\\" + $FileName.Replace(".", "\.") + "['"" ]\s*")
Write-Host $Arguments

Note that there are a few pitfalls with regards to the command filename in the command line:

  • it might contain a relative or absolute path
  • it might be quoted or not

6 Comments

Please help me to understand what you mean with $MyInvocation? The fixed path to my PowerShell script? Something like $MyInvocation = "D:\echoArgs1.ps1"?
$MyInvocation contains an information about the current command, such as the name, parameters, parameter values, and information about how the command was started, called, or "invoked," such as the name of the script that called the current command.
It works running .\echoArgs1.ps1 from an open Powershell session. However, if I run powershell -file echoArgs1.ps1 "Hello "W"orld" from a cmd prompt then $MyInvocation.Line seems to be empty.
if ( $MyInvocation.Line) { $MyInvocationLine = $MyInvocation.Line } else { $MyInvocationLine = (Get-WmiObject -Query "SELECT CommandLine FROM Win32_Process WHERE ProcessID = $PID").CommandLine }; $Arguments = $MyInvocationLine -Replace ("^.*\\" + $FileName.Replace(".", "\.") + "['"" ]\s*")
Inspecting the raw command line is a great idea, but in the given scenario [Environment]::CommandLine is the simplest solution. I don't think cmd.exe is involved at all when an external tool is invoked from Visual Studio.
|

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.