2

Ok, so I'm spitting command line arguments by spaces, like Command Prompt, but the problem is, if the user tries to type in DoStuff "argument that has spaces but is quoted" it will not split it correctly. I am using a console application. I've tried to do it like this: baseCommand is the string that the user types in unparsed, and secondCommand is supposed to be the second argument.

int firstQuoteIndex = baseCommand.IndexOf('"');

if (firstQuoteIndex != -1)
{
    int secondQuoteIndex = baseCommand.LastIndexOf('"');
    secondCommand = baseCommand.Substring(firstQuoteIndex, 
        secondQuoteIndex - firstQuoteIndex + 1).Replace("\"", "");
}

This works well, but first, it's messy, and second, I'm not sure how to do this if the user types in something like this:

DoSomething "second arg that has spaces" "third arg that has spaces"

Keep in mind that the user doesn't have to type in quotes if the arg(s) don't have quotes. Does anybody have any suggestions, Thanks.

Anu Viswan
  • 17,797
  • 2
  • 22
  • 51
Visual User
  • 69
  • 1
  • 6
  • Last is not the same as next... https://stackoverflow.com/questions/22669044/how-to-get-the-index-of-second-comma-in-a-string – Alexei Levenkov Jan 08 '20 at 02:07
  • You're only capturing the fist and last quote. Instead, you should continue to use `IndexOf`, starting at the position after the previous quote. – Rufus L Jan 08 '20 at 02:08

2 Answers2

9

You could use following Regex for the purpose.

[\""].+?[\""]|[^ ]+

For example,

var commandList = Regex.Matches(baseCommand, @"[\""].+?[\""]|[^ ]+")
                .Cast<Match>()
                .Select(x => x.Value.Trim('"'))
                .ToList();

Sample 1 Input

DoSomething "second arg that has spaces" thirdArgumentWithoutSpaces

Output

Command List
------------
DoSomething 
second arg that has spaces
thirdArgumentWithoutSpaces

Sample 2 Input

DoSomething "second arg that has spaces" "third Argument With Spaces"

Output

Command List
------------
DoSomething 
second arg that has spaces
third Argument With Spaces
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Anu Viswan
  • 17,797
  • 2
  • 22
  • 51
3

I'm sure there's a more elegant way to do this, but one way is to simply walk through your command line one character at a time, keeping track if you're "inside a quoted string", and simply build the arguments as you go, adding them to a list when you encounter an end-quote or a space that's not inside a quoted string.

For example:

public static List<string> ParseCommandLine(string cmdLine)
{
    var args = new List<string>();
    if (string.IsNullOrWhiteSpace(cmdLine)) return args;

    var currentArg = new StringBuilder();
    bool inQuotedArg = false;

    for (int i = 0; i < cmdLine.Length; i++)
    {
        if (cmdLine[i] == '"')
        {
            if (inQuotedArg)
            {
                args.Add(currentArg.ToString());
                currentArg = new StringBuilder();
                inQuotedArg = false;
            }
            else
            {
                inQuotedArg = true;
            }
        }
        else if (cmdLine[i] == ' ')
        {
            if (inQuotedArg)
            {
                currentArg.Append(cmdLine[i]);
            }
            else if (currentArg.Length > 0)
            {
                args.Add(currentArg.ToString());
                currentArg = new StringBuilder();
            }
        }
        else
        {
            currentArg.Append(cmdLine[i]);
        }
    }

    if (currentArg.Length > 0) args.Add(currentArg.ToString());

    return args;
}

Usage might look like:

public static void Main()
{
    var args = "one two \"three four\" five \"six seven\" eight \"nine ten";

    Console.WriteLine($"Command line: \"{args}\"\n");
    Console.WriteLine("Individual Arguments");
    Console.WriteLine("--------------------");
    Console.WriteLine(string.Join(Environment.NewLine, ParseCommandLine(args)));

    GetKeyFromUser("\nDone! Press any key to exit...");
}

Output

enter image description here

Rufus L
  • 36,127
  • 5
  • 30
  • 43