4

I know there were similar questions, but there is one thing I can't find in the answers. I'm trying to pass the following string to batch file:

hello " world

This is a single argument. Here is my batch file:

@echo off
@echo %1

Now if I run it from command line, I'm getting the following results:

>C:\file.bat "hello "" world"
"hello "" world"

>C:\file.bat "hello \" world"
"hello \"

>C:\file.bat "hello """ world"
"hello """

In all cases I'm getting the wrong result, how do I escape a double quote and pass it correctly? Or should I do any additional conversion steps in the batch file itself?

username
  • 3,378
  • 5
  • 44
  • 75

2 Answers2

7

It is impossible to pass a string literal value of hello " world as a batch parameter for two reasons:

  1. Space cannot be escaped with regard to parameter tokenization. Parameters are always delimited by unquoted token delimiters, even if they are preceded by the ^ escape character. The token parameter token delimiters are {space}, {tab}, ,, ; =, and {0xFF}. The only way to include a token delimiter as part of a parameter is to make sure the delimiter is quoted.

  2. It is impossible to escape a quote within a quoted string. Quotes are a state machine: The first encountered quote turns on quote semantics, and the subsequent one turns it off. The next quote turns it back on again, etc. If quoting is off, then a quote literal can be escaped as ^" to keep quoting off. But once quoting has begun, then there is no way to escape a closing quote: The next quote always turns quoting off.

So no matter how you add quotes and/or escaped quotes, there will always be an unquoted space, thus the string will be treated as two parameters.

You could adopt the strategy recommended by Hapax - Quote the entire string, and double any quote literals within the string: "Hello "" world".

However I would make a slight change and use %~1 instead of %1 so as to remove the outer quotes. Then the doubled quotes should be converted back to a single quote:

@echo off
set "arg1=%~1"
set "arg1=%arg1:""="%"
echo %arg1%

But there are other potential issues with passing string literals as batch parameters. The most insidious is caret doubling. It is impossible to pass a quoted caret as a parameter if the caller uses CALL. I won't get into the mechanism behind the problem. See https://stackoverflow.com/a/4095133/1012053 if you want more information. But the following illustrates the problem:

test.bat

@echo %1

-- Sample cmd session --

D:\test>test "^"
"^"

D:\test>call test "^"
"^^"

Because of the many complications with passing string literals as batch parameters, the most effective strategy used in advanced batch scripting is to store the value in an environment variable, and then pass the variable name as the parameter. The batch script can then use delayed expansion to get the correct value:

test2.bat

@echo off
setlocal enableDelayedExpansion
set "arg1=!%1!"
echo !arg1!

-- Sample cmd session --

D:\test>set "myVar=Hello world! ^&|<>" How are you doing? !^^^&^|^<^>^"

D:\test>set myVar
myVar=Hello world! ^&|<>" How are you doing? !^&|<>

D:\test>test2 myVar
Hello world! ^&|<>" How are you doing? !^&|<>

D:\test>call test2 myVar
Hello world! ^&|<>" How are you doing? !^&|<>
dbenham
  • 127,446
  • 28
  • 251
  • 390
  • 1
    Correct me if I'm wrong, but in your first code block, shouldn't `set "arg1=%arg1:"=%"` be `set "arg1=%arg1:""="%"` (doubling the single interior doublequote, and adding a doublequote before the trailing `%`), matching the similar line in [Hapaxia's answer](https://stackoverflow.com/a/36428055/364696)? As written, you're replacing *all* doublequotes with nothing (after stripping outer doublequotes), rather than replacing interior doubled-doublequotes with single-doublequotes. That is, this would parse `"Hello""World"` to `HelloWorld`, not `Hello"World`. – ShadowRanger May 02 '18 at 15:23
  • 1
    @ShadowRanger - You are absolutely correct. Everything I wrote was correct, but then I provided disfunctional code that did not match my description. I've updated the code exactly as you said to match my original intent. Thanks for finding and reporting this. – dbenham May 03 '18 at 12:26
2

The way to keep quotes is to pass them doubled. However, this passes both of them. You can then process the input and remove the doubles.

Executing using file.bat "hello "" world", we use:

@echo off
set param=%1
set param=%param:""="%
echo %param%

(result: "hello " world")

A shorter version:

@echo off
set param=%1
echo %param:""="%
Hapaxia
  • 155
  • 1
  • 11
  • Does this mean that I need to manually convert any escaped sequence to its original form when I pass something to batch file? – username Apr 05 '16 at 13:42
  • Lordy, we're all still struggling to understand what circumstances would make it desirable to tokenize a single quotation mark character as a script argument. @username, I'm pretty sure the application of any advice offered on this page will be completely your discretion, since only you understand the madness of your plans. This whole thing seems like an [XY problem](http://xyproblem.info/) to me. – rojo Apr 05 '16 at 13:51
  • @rojo my X problem is to fix a piece of legacy software that already does that Y thing. I can't ask X until I understand that Y is bad. I thought it is possible to workaround it somehow, but it seems that the whole idea of passing params to batch files is problematic. – username Apr 05 '16 at 14:14
  • @username I can't say I've ever had much difficulty passing arguments to batch files. What's problematic about it? Quotation marks are used to prevent evaluation of some characters (such as spaces, ampersands, pipes, and others). `%0` through `%9` act as `argv[0]` through `argv[9]`, and `%*` gives you access to all arguments. Mismatched quotes are tricky, but it's also reasonable to consider them a syntax error on the part of the user. – rojo Apr 05 '16 at 14:24
  • @rojo It is problematic in a sense that you need to convert arguments back to original form in a batch file. It's just not what I expected to see. – username Apr 05 '16 at 14:40
  • @username Yeah, you lost me again. Convert? Original form? I'm not aware of any form morphing. Save `@echo %*` as test.bat, then from the cmd line enter `test Hello " World!`. The garbage that goes in is the same garbage that comes back out -- and without any sort of suspicious-quote "conversion". [XY problem](http://meta.stackexchange.com/a/66378/275822). – rojo Apr 05 '16 at 14:55
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/108293/discussion-between-username-and-rojo). – username Apr 05 '16 at 15:02
  • 4
    You should use `set param=%~1`. You don't want the outer quotes to be part of the value. – dbenham Apr 05 '16 at 22:26
  • I agree. I upvoted your own answer since it contains all the information and more. – Hapaxia Apr 08 '16 at 02:53