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

nixda
  • 2,654
  • 12
  • 49
  • 82
  • 2
    How exactly is the input string containing the quotes obtained and passed to powershell? – Mathias R. Jessen Aug 17 '19 at 12:33
  • `powershell -file echoArgs1.ps1 "Hello """W"""orld"` should work (note _tripled_ inner `"`s). – JosefZ Aug 17 '19 at 12:47
  • 1
    @JosefZ I explicitely said *I have no control over the input string* – nixda Aug 17 '19 at 12:59
  • 1
    @MathiasR.Jessen The input string comes via Visual Studio and the integrated [External Tools](https://learn.microsoft.com/en-us/visualstudio/ide/managing-external-tools?view=vs-2019) 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)"` – nixda Aug 17 '19 at 13:08
  • Run `powershell -file echoArgs1.ps1 "%_a:"="""%"` supposing that you have the string in a windows environment variable e.g. as `set "_a=Hello "W"orld"`. – JosefZ Aug 17 '19 at 14:50
  • 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. – mklement0 Aug 18 '19 at 23:13

2 Answers2

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]
mklement0
  • 382,024
  • 64
  • 607
  • 775
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
iRon
  • 20,463
  • 10
  • 53
  • 79
  • Please help me to understand what you mean with `$MyInvocation`? The fixed path to my PowerShell script? Something like `$MyInvocation = "D:\echoArgs1.ps1"`? – nixda Aug 17 '19 at 13:10
  • [`$MyInvocation`](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_automatic_variables?view=powershell-6#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. – iRon Aug 17 '19 at 13:13
  • 2
    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. – JosefZ Aug 17 '19 at 13:35
  • 1
    `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*")` – JosefZ Aug 17 '19 at 17:59
  • @JosefZ, I think you should add this as your own answer and earn the credits for this. Small note: it doesn't work correctly from a PowerShell prompt (*some* quotes are removed), but works fine from a CMD prompt which is indeed the question (in other words, it is very depended on the host were it is executed). – iRon Aug 18 '19 at 08:19
  • 1
    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. – mklement0 Aug 18 '19 at 23:32