Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parsing Text and Multiple Parameters #9

Open
Morgma opened this issue Nov 6, 2012 · 6 comments
Open

Parsing Text and Multiple Parameters #9

Morgma opened this issue Nov 6, 2012 · 6 comments

Comments

@Morgma
Copy link

Morgma commented Nov 6, 2012

Command parameters are not parsed entirely as I would expect and I do not see anything in the documentation to point me to a solution at this point.

Comments in the source of ManyConsole suggest that the behavior for parsing parameters should be as such:

// this command
my-command --integer 1 --string "some long text value" --collection param1 param2 param3

// should evaluate to
--integer == 1
--string == "some long text value"
--collection = ["param1", "param2", "param3"]

http://msdn.microsoft.com/en-us/library/system.environment.getcommandlineargs.aspx

Instead, the option --string ignores any quote and parses the space delimiter.

// actual parameterization
--string == "some
--collection == param1

// then everything else falls into a generic container:
AdditionalArguments == long, text, value", param2, param3

How can several values be collected in one parameter call?

// In the derived ConsoleCommand constructor I want to avoid something like:
my-command --string some --string long --string text --string value

// and then doing:
HasOption<string>("s|string=", v => StringList.Add(v));

// finally:
var stringValue = string.Join(" ", StringList);

I could parse out the remaining arguments after all single value command options are captured, but it will be difficult to come up with a constant number of parameters for the "HasAdditionalArguments" override.

Here's a sample of what I'm doing:

public class TestCommand : ConsoleCommand {

        public TestCommand() {
            IsCommand("my-command");

            StringList = new List<string>();
            HasOption("s|string=", "captures a text parameter", s => StringList.Add(s));
        }

        public List<string> StringList; 
        public override int Run(string[] remainingArguments) {
            var stringValue = string.Join(" ", StringList);

            System.Console.WriteLine("Received input: " + stringValue);
            return 0;
        }
    }

Let me know if I'm missing any existing configuration here. Perhaps I can override the argument delimiter per option?

@fschwiet
Copy link
Owner

fschwiet commented Nov 7, 2012

Hi Matt. Let me see if I can help.

When you're not sure how many additional arguments there are, you can use
AllowsAnyAdditionalArguments (See
https://github.com/fschwiet/ManyConsole/blob/master/SampleConsole/MattsCommand.csas
an example). I'm afraid I don't know of a way to specify a parameter
could have multiple values (--collection one two three), the closest I can
suggest is what I linked.

With regards to the first issue, I'd expect my-command --string "some
long text value" to give you the full string that is within the quotes. I
added a string parameter to the sample command I just linked in order to
verify this is the case. I'm not sure why you're only seeing '"some', but
maybe if you can provide sample code I'd be able to figure out how that is
happening. Right now I'm not able to reproduce the problem.

good luck

On Tue, Nov 6, 2012 at 8:47 AM, Matthew Morgan [email protected]:

Command parameters are not parsed entirely as I would expect and I do not
see anything in the documentation to point me to a solution at this point.

Comments in the source of ManyConsole suggest that the behavior for
parsing parameters should be as such:

// this command
my-command --integer 1 --string "some long text value" --collection param1
param2 param3

// should evaluate to
--integer == 1
--string == "some long text value"
--collection = ["param1", "param2", "param3"]

http://msdn.microsoft.com/en-us/library/system.environment.getcommandlineargs.aspx

Instead, the option --string ignores any quote and parses the space
delimiter.

// actual parameterization
--string == "some
--collection == param1

// then everything else falls into a generic container:
AdditionalArguments == long, text, value", param2, param3

How can several values be collected in one parameter call?

// In the derived ConsoleCommand constructor I want to avoid something
like:
my-command --string some --string long --string text --string value

// and then doing:
HasOption("s|string=", v => StringList.Add(v));

// finally:
var stringValue = string.Join(" ", StringList);

I could parse out the remaining arguments after all single value command
options are captured, but it will be difficult to come up with a constant
number of parameters for the "HasAdditionalArguments" override.

Here's a sample of what I'm doing:

public class TestCommand : ConsoleCommand {

    public TestCommand() {
        IsCommand("my-command");

        StringList = new List<string>();
        HasOption("s|string=", "captures a text parameter", s => StringList.Add(s));
    }

    public List<string> StringList;
    public override int Run(string[] remainingArguments) {
        var stringValue = string.Join(" ", StringList);

        System.Console.WriteLine("Received input: " + stringValue);
        return 0;
    }
}

Let me know if I'm missing any existing configuration here. Perhaps I can
override the argument delimiter per option?


Reply to this email directly or view it on GitHubhttps://github.com//issues/9.

@fschwiet
Copy link
Owner

fschwiet commented Nov 7, 2012

Replying in email kind of mangled message, reposting:

Hi Matt. Let me see if I can help.

When you're not sure how many additional arguments there are, you can use AllowsAnyAdditionalArguments (See https://github.com/fschwiet/ManyConsole/blob/master/SampleConsole/MattsCommand.cs as an example). I'm afraid I don't know of a way to specify a parameter could have multiple values (--collection one two three), the closest I can suggest is what I linked.

With regards to the first issue, I'd expect my-command --string "some long text value" to give you the full string that is within the quotes. I added a string parameter to the sample command I just linked in order to verify this is the case. I'm not sure why you're only seeing '"some', but maybe if you can provide sample code I'd be able to figure out how that is happening. Right now I'm not able to reproduce the problem.

@Morgma
Copy link
Author

Morgma commented Nov 12, 2012

I apologize for taking so long to reply.

I've included code for a complete working example. I added a bit of parsing logic to try to get the output I expect, but parsing the additional parameters is not reliable as we are expecting that only one command may accept a text parameter and use "remainingArguments[]" as a container.

Console Program:

using System;
using System.Collections.Generic;
using System.Linq;
using ManyConsole;

namespace ManyConsoleExamples {
    internal class Program {
        private static void Main(string[] args) {

            if (!args.Any()) {
                System.Console.WriteLine("Please enter a command or enter q to quit.");
                args = System.Console.ReadLine().Split(' ');

                if (args[0] == "q")
                    return;
            }

            // Locate any commands in the assembly
            IEnumerable<ConsoleCommand> commands = GetCommands();

            var consoleRunner = new ConsoleModeCommand(GetCommands);

            commands = commands.Concat(new[] { consoleRunner });

            // run the command
            try {
                ConsoleCommandDispatcher.DispatchCommand(commands, args, System.Console.Out);
            }
            catch (Exception ex) {
                System.Console.WriteLine(@"Command failed to execute. Please see the following exception.");
                System.Console.WriteLine(@"--------------------------------------------------------------");
                System.Console.WriteLine(ex);
            }
            finally {
                args = new string[] { }; // clear arguments
            }

            if (!Environment.UserInteractive) // allow for use as an api
                return;

            Main(args); // callback with empty argument set to reset the console
        }

        private static IEnumerable<ConsoleCommand> GetCommands() {
            return ConsoleCommandDispatcher.FindCommandsInSameAssemblyAs(typeof(Program));
        }

    }
}

Command Class:

using System;
using ManyConsole;

namespace ManyConsoleExamples.Commands {
    public class AcceptsLongStringParameter : ConsoleCommand {
        public string comments;

        public AcceptsLongStringParameter() {
            IsCommand("echo");

            HasOption("c|comment=",
                           "enter a comment delimited by double quotes, i.e. - \"See Matt's poorly written code\"",
                           v => comments = v);

            AllowsAnyAdditionalArguments("<foo1> <foo2> <fooN> where N is a word");
        }

        public override int Run(string[] remainingArguments) {

            if(string.IsNullOrWhiteSpace(comments)) {
                Console.WriteLine("You made no comment");
                return 0;
            }

            // I'm parsing the remainingArguments[] here to give you an example output that more closely represents what I
            // would expect to happen by default when containing a string parameter in double quotes
            Console.WriteLine("Your comment is: " + comments + " " + String.Join(", ", remainingArguments).Replace(',', ' '));
            return 0;
        }
    }
}

You can see here that the only captured argument is "hello and the remaining arguments are there and world"

Example Output

@fschwiet
Copy link
Owner

I see, it seems this problem is specific to when commands are run via the ConsoleModeCommand, and not from the command line. The fix would likely belong in https://github.com/fschwiet/ManyConsole/blob/master/ManyConsole/Internal/StringExtensions.cs, where ToCommandLineArgs are used by ConsoleModeCommand.

That function depends on CommandLineToArgvW, which we may be using incorrectly. If you're able to dig in and find a solution that'd be great, I'm not sure when I can look at this though I might this weekend. I wonder if the problem is related to http://blogs.msdn.com/b/oldnewthing/archive/2010/09/16/10062818.aspx? It seems like we do have the bug mentioned there, it might be worthwhile adding "fake.exe " to the beginning of the input and then dropping that segment within ToCommandLineArgs.

@fschwiet
Copy link
Owner

Can you get the latest version of the source and see if you can repro this with SampleCommand? When I run it, whether via powershell, cmd or some other shell I get:

C:> .\SampleConsole.exe run-console

Executing run-console (Run in console mode, treating each line of console input as a command.):

Enter a command or 'x' to exit or '?' for help
echo -c "one two three" "four five six" seven eight nine

Executing echo:
comments : one two three

Your comment is: one two three
The remaining arguments were ["four five six","seven","eight","nine"]

Enter a command or 'x' to exit or '?' for help

@fschwiet
Copy link
Owner

(this seems like the expected result, and not what you were getting"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants