16

Question: Given a file name and an arbitrary list of strings, is there a canonical way to create a single command line such that Environment.GetCommandLineArgs (and C#'s void main(String[] args)/ VB's Sub Main(args() As String)) will return the same list of strings?


Background: The way .NET splits a command line into arguments is surprisingly complex, e.g.:

If a double quotation mark follows two or an even number of backslashes, each proceeding backslash pair is replaced with one backslash and the double quotation mark is removed. If a double quotation mark follows an odd number of backslashes, including just one, each preceding pair is replaced with one backslash and the remaining backslash is removed; however, in this case the double quotation mark is not removed.

Many try the simple "put every argument in double quotes and escape existing double quotes" approach and fail as soon as one of the arguments contains a trailing backslash. There have been various questions on StackOverflow regarding this issue, e.g.:

However, their answers are either not general enough to provide a canonical solution for all cases or appear to be developed "iteratively" ("Oh, there's one more special case I forgot, let's add it and now it should cover most cases..."). Since this is quite a common problem, I'd like to see a solution that provides confidence, for example, by either

  • coming from an authoritative source (maybe a blog entry from one of the developers involved in this crazy command line convention) or
  • providing a formal proof that the given algorithm satisfies the .NET command line requirements.
Community
  • 1
  • 1
Heinzi
  • 167,459
  • 57
  • 363
  • 519
  • Pursuing a generic solution to this problem is very unproductive. Write data to a file, pass the path of the file instead. – Hans Passant Dec 10 '12 at 14:02
  • I agree with Hans. What would you do with the solution once you had it? – neontapir Dec 10 '12 at 21:14
  • 3
    @HansPassant: You don't always have control over the *called* application (and many applications out there today *do* use multiple command-line arguments). Thus, the writer of the *calling* application might want to ensure that the arguments are encoded correctly (ideally, without having to reinvent the wheel and study the Windows/.NET command line escaping syntax in detail every time). – Heinzi Dec 11 '12 at 08:20

3 Answers3

3

This algorithm is generic and comes from a relatively authoritative source (MSDN blogs).

Anton Tykhyy
  • 19,370
  • 5
  • 54
  • 56
  • Thanks, that looks useful. However, it raises the question whether [CommandLineToArgvW](http://msdn.microsoft.com/en-us/library/bb776391%28v=vs.85%29.aspx) and [Environment.GetCommandLineArgs](http://msdn.microsoft.com/en-us/library/system.environment.getcommandlineargs.aspx) behave the same. Some sources claim they do, but the documentation seems to differ in the case of "an even number of backslashes plus a quotation mark". – Heinzi Dec 11 '12 at 08:23
  • Take a look at [documentation for older versions of .NET](http://msdn.microsoft.com/en-us/library/system.environment.getcommandlineargs%28v=vs.80%29.aspx). Different versions document different behavior. It's a mess :) – Anton Tykhyy Dec 11 '12 at 19:06
1

A few years ago, Microsoft announced that they were going to release a command line parser on CodePlex (instead of the System.Shell.CommandLine that was supposed to ship with .NET Framework 4). I'm not sure if they actually did this. If you want a parser developed by a Microsoft employee have a look at cmdline. Also you can look for command line parses in CodePlex.

You could also try Mono.Options which is quite powerful.

Panos Rontogiannis
  • 4,154
  • 1
  • 24
  • 29
0

Starting with .NET Core 2.1, it's no longer necessary to escape command line arguments manually: ProcessStartInfo now has an ArgumentList property and automatically takes care of any escaping required.

To quote the example from the documentation:

var info = new System.Diagnostics.ProcessStartInfo("cmd.exe");
info.ArgumentList.Add("/c");
info.ArgumentList.Add("dir");
info.ArgumentList.Add(@"C:\Program Files\dotnet"); // there is no need to escape the space, the API takes care of it

Here you can see the algorithm in all its glory (including the famous "2n+1 backslash rule"). It's MIT licensed, which makes it easy to backport it to .NET Framework 4.8 projects, if requied.

Heinzi
  • 167,459
  • 57
  • 363
  • 519