44601

Why does trailing backslash followed by quote behave so strangely in cmd?

Question:

With this program (cs.exe):

class Program { static void Main(string[] args) { foreach (var item in args) { Console.WriteLine(item); } } }

And run:

> cs.exe go\to\a_path go\to\a_path > cs.exe "go\to\a path" go\to\a path > cs.exe "go\to\a path\" go\to\a path" > cs.exe 'go\to\a path\' 'go\to\a path\'

That means if your path has a space so you quote it, be very careful NOT to put a trailing \ at the end, otherwise your program might just not be able to handle it as it incorrectly contains a " at the end. Single quote is even weirder!

PowerShell exhibits a similar behavior but without the difference between single and double quotes.

How do I understand this behavior? What's the underlying rule to evaluate backslash in cmd so this can be explained consistently?

Answer1:

As you are not calling an internal cmd command, but calling an executable file, this behaviour is not caused by cmd but the command line argument parser routines. In windows, programs don't receive a collection/array/set of arguments, but a string with all the arguments and each program tokenizes this string to obtain each element. This is usually done by routines included by the compiler that hides this operation and exposes to the code an easier way to handle arguments.

Documentation for the <a href="https://msdn.microsoft.com/en-us/library/a1y7w461.aspx" rel="nofollow">C Command-Line argument parser</a> states that

<blockquote> <ul><li>

Arguments are delimited by white space, which is either a space or a tab.

</li> <li>

A string surrounded by double quotation marks is interpreted as a single argument, regardless of white space contained within. A quoted string can be embedded in an argument. Note that the caret (^) is not recognized as an escape character or delimiter.

</li> <li>

A double quotation mark preceded by a backslash, \", is interpreted as a literal double quotation mark (").

</li> <li>

Backslashes are interpreted literally, unless they immediately precede a double quotation mark.

</li> <li>

If an even number of backslashes is followed by a double quotation mark, then one backslash (\) is placed in the argv array for every pair of backslashes (\\), and the double quotation mark (") is interpreted as a string delimiter.

</li> <li>

If an odd number of backslashes is followed by a double quotation mark, then one backslash (\) is placed in the argv array for every pair of backslashes (\\) and the double quotation mark is interpreted as an escape sequence by the remaining backslash, causing a literal double quotation mark (") to be placed in argv.

</li> </ul></blockquote>

There is also a set of undocumented/non official rules (<a href="http://www.daviddeley.com/autohotkey/parameters/parameters.htm" rel="nofollow">How Command Line Parameters Are Parsed</a>)

<blockquote> <ul><li>Outside a double quoted block a " starts a double quoted block.</li> <li>Inside a double quoted block a " followed by a different character (not another ") ends the double quoted block.</li> <li>Inside a double quoted block a " followed immediately by another " (i.e. "") causes a single " to be added to the output, and the double quoted block continues.</li> </ul></blockquote>

The .Net argument parsing <a href="https://msdn.microsoft.com/en-us/library/system.environment.getcommandlineargs%28v=vs.110%29.aspx" rel="nofollow">rules</a> are just a derivation of those rules. If you need a different behaviour, then you should use the <a href="https://msdn.microsoft.com/en-us/library/system.environment.commandline%28v=vs.110%29.aspx" rel="nofollow">Environment.CommandLine</a> property to retrieve the full command line string and write your own parsing code.

Recommend

  • PHP set_time_limit no effect
  • MySQL Amazon RDS: Lock Wait timeout exceeded
  • VS 2015, C# 6, MVC5, Roslyn — 502 gateway errors on Azure web app
  • readPNG error: ggmap and Stamen Maps
  • How to show special escape characters like LineBreak in Java output?
  • Postgresql: Invalid regular expression: invalid backreference number
  • Can comments make any difference during the run-time?
  • JavaFX ComboBox setItems triggers onAction event
  • How does MemberWiseClone create a new object with the cloned properties?
  • Can I call custom javascript from an R jupyter notebook
  • Group variable in cobol
  • 'include' of functions in groovy scripts
  • View Paypal shopping cart contents on my site
  • Zeromq with python hangs if connecting to invalid socket
  • Timeout a query
  • iOS Localization Doesn't Work with More Than 63 Files
  • F#: In which memory area is the continuation stored: stack or heap?
  • Unable to play media with vlc ocx
  • Divide a $1 by 3 and adjusting 1 cent
  • why calling cd shell command through system() or execvp() from a child process won't work?
  • Get the number 18437736874454810627
  • BeautifulSoup difference between findAll and findChildren
  • VSCode change debug shell to bash on windows
  • Error processing multiple files
  • Jquery Knockout: ko.computed() vs classic function?
  • aapt.exe'' finished with non-zero exit value 1
  • Is there some graphical way to create my own configuration file on SonarLint?
  • SAXReader not re-ecape characters
  • Android app gives error “BatteryStatsImpl: reading network stats”
  • Java color detection
  • Apache RewriteRule redirection with url encoded
  • zope_i18n_compile_mo_files doesn't work on a Zeo configuration
  • OOP Javascript - Is “get property” method necessary?
  • How to run “Deployd” on port 80 instead of port 5000 in webserver.
  • Scrapy recursive link crawler
  • SignalR .NET Client Invoke throws an exception
  • formatting the colorbar ticklabels with SymLogNorm normalization in matplotlib
  • QLineEdit password safety
  • Why winpcap requires both .lib and .dll to run?
  • CSS Applying specific rule for a specific monitor resolution with only CSS is posible?