|
|
This is near perfect, at least as advertised.
There is another layer I'd add though - using System.Attribute derivatives to create command line argument switches from fields using reflection. That way the entire thing can be declarative
You got my 5 vote because you saved me a ton of time.
Real programmers use butterflies
|
|
|
|
|
This does go into detail what it wants but for the love of my life I just cannot figure out what I am doing wrong.
I included the resx file and everything.
Tried changing program.cs namespace and practically any other namespace, I just don't understand.
I'm an utter beginner at this so sorry if I make you cringe over this lackluster error.
|
|
|
|
|
Nice work, thank you for publishing it, it saved me some time that's for sure
|
|
|
|
|
I'm having a problem with command lines that have more than one parameter of type Bool.
If I define these parameters:
_cmdLine.Parameter(CLAParser.CLAParser.ParamAllowType.Optional, "p1", CLAParser.CLAParser.ValueType.Bool, "Parm 1");
_cmdLine.Parameter(CLAParser.CLAParser.ParamAllowType.Optional, "p2", CLAParser.CLAParser.ValueType.Bool, "Parm 2");
_cmdLine.Parameter(CLAParser.CLAParser.ParamAllowType.Optional, "p3", CLAParser.CLAParser.ValueType.Bool, "Parm 3");
I can do this:
prog -p1 -p2
But this:
prog -p1 -p2 -p3 gives me this error "Command Line Argument Error: Invalid value found. Parameter "p2" must not have a value!"
Any ideas?
|
|
|
|
|
Thanks for your error report. There was still an error in the regular expression. It should be fixed now.
|
|
|
|
|
If you don't use complex command line arguments, consider use of a very Simple Command Line Arguments Parser[^]. It can be used as a foundation for your own application specific parameter presenter.
|
|
|
|
|
Everybody has written a command line parser. I did create a console application template with a command line parser which is I believe the most simple and easy to understand one.
It goes basically like
public Program(string [] args)
{
var switches = new Dictionary
{
{"OptionaL" , () => Optional=true },
};
var switchWithArg = new Dictionary >
{
{"ArgSwitch" , (arg) => ArgSwitch = arg },
};
Action > rest = (parameters) => OtherArgs = parameters;
ArgParser parser = new ArgParser (switches, switchWithArg, rest);
if (!parser.ParseArgs(args))
{
parser.PrintHelpWithMessages(HelpStr);
return ;
}
if (!ValidateArgs(parser))
{
parser.PrintHelpWithMessages(null );
}
else
{
Run();
}
}
Here is a more thorough explanation:
http://geekswithblogs.net/akraus1/archive/2010/11/11/142685.aspx[^]
|
|
|
|
|
You are right, there are tons of command line parsers! Thank for sharing yours. It is very nice.
Which one is easier to use is just personal taste I would say. I like the shortcuts of your parser, whereas with CLAParser I like that you do not have to define variables for each argument as well as the integrated construction of the help output.
Which one is more functional I do not dare to say. My guess is that generic but simple approaches - like ours - always come with some limitation. E.g. mine cannot handle two files with spaces, like prog.exe "file 1" "file 2"; yours cannot handle switches after other arguments, like prog.exe file.txt /a, can it?
|
|
|
|
|
Yes you are right there is no right parser for everybody. As you correctly pointed out each one has its own set of limitations.
Yours,
Alois Kraus
|
|
|
|
|
Hello Julian,
I tried to parse program.exe /help and got some exception:
Ex.Message = "Command Line Argument Error: Value without parameter found: \"/help\". Each value must be assigned to a parameter!"
I figured out, that parsing created an unknown value, but it should be a parameter.
If e.g. some parameter has been matched before the valueless parameter, it works fine (e.g. program.exe /all 1 2 /help
So I modified the regex for:
4) or anything that contains no space [^ ]*?
to:
4) or anything that contains no space but does not start with / or - [[^/^-]^ ]*? ... valueless parameter (e.h. /help).
... this works fine for program.exe /help
Hope I didn't miss a thing ...
Regards and thanks for posting it,
Martin
|
|
|
|
|
Hi Martin,
thanks for your post! A bummer you found there!
However, hanging the regex to ignore leading / and - as you propose would mean that you cannot specify an absolute (Linux style) path, e.g: program.exe /etc/usr/
Well, it might be unlikely that anybody would want to do it, nevertheless, I don't like to swallow anything there.
I think its a structural problem you detected and solving it right would involve not distinguishing between the first parameter and following parameter-value-pairs. But since that would - no doubt - trigger further changes, I came up with a work around. It involves re-regexing the first detected parameter. Not very nice but it should work. The article and the download archive is being updated shortly.
Thanks again!
Julian
|
|
|
|
|
I like the implementation. Have you checked the code for FxCop compliance?
|
|
|
|
|
Dave Hary wrote: Have you checked the code for FxCop compliance?
Who gives a sh$t about FxCop? If you want FxCop compliance, surely you can do it yourself.
|
|
|
|
|
I was going to say the same thing.
|
|
|
|
|
Hi Dave,
thanks for the suggestion. It might be nice to have the code checked by FxCop. However, it has (at this time) no priority for me. Sorry.
Regards,
Julian
|
|
|
|
|
You might want to consider working with a more "raw" version of the command line that goes into your .Parse so you can have more consistent parsing. If you use the snippet below, you won't have to worry about what pre-processing .NET might have done to the args[] (like string escape handling). I ran out of time so I don't know what code changes are required to support this in your library.
static public string GetRawCommandlineArgs() {
string Raw = Environment.CommandLine;
string ExecutablePath = Environment.GetCommandLineArgs()[0];
string Parsed;
if (Raw.StartsWith("\"")) {
Parsed = Raw.Substring(ExecutablePath.Length + 2);
} else {
Parsed = Raw.Substring(ExecutablePath.Length);
}
Parsed = Parsed.TrimStart(' ');
return Parsed;
}
The whole reason I went down this path is because for the life of me I couldn't figure out how to pass these parameters!
This will return 'Unknown parameter found: "01\product;Integrated".'
<br />
my.exe /ConnectionString "Data Source=Server-01\product;Integrated Security=true;" /ScriptFile "file-00.ext"<br />
This will return 'A required parameter is missing! Parameter "ScriptFile" is required but missing!'
<br />
my.exe /ConnectionString "\"Data Source=Server-01\product;Integrated Security=true;\"" /ScriptFile "\"file-00.ext\""<br />
|
|
|
|
|
I am sorry. You are right. There was an error in the regular expression which caused you problem. Otherwise your example should have worked.
An update is on its way. It also includes your function GetRawCommandlineArgs(). Please use function Parse() now (without parameter). Thank you very much for your help!
|
|
|
|
|
This is exactly what I as looking for. Saved me half a day on a project that needed to be finished yesterday.
|
|
|
|
|
Nice article, I like what you've got brewing
However, what about a console application which performs multiple functions with their own arguments? Maybe I played an ignorant eye but it seems that the parser is only for a single set of arguments/values. So what if there are different contexts the console app can take?
Example, you have an application which processes intermediate data for various input data (geometry, sound, images, etc) which require their own respective arguments? What about having factory of a sort (so you're only defining one entry-point object) which you can define these contexts (and thus their arguments) with? If in the event the application only has one purpose you could have a "default" property for a context which it defaults to.
Basically the first argument would define the context which to look up (ie, import-sound) then any following arguments would just act as that specific context's arguments and thus the parsing would execute on.
|
|
|
|
|
You mean like the "net" command? For example, "net accounts" has a completely different set of parameters vs. "net config". Just entering "net" produces this:
NET [ ACCOUNTS | COMPUTER | CONFIG | CONTINUE | FILE | GROUP | HELP |
HELPMSG | LOCALGROUP | NAME | PAUSE | PRINT | SEND | SESSION |
SHARE | START | STATISTICS | STOP | TIME | USE | USER | VIEW ]
|
|
|
|
|
|
> it seems that the parser is only for a single set of arguments/values
Yes, that is correct. There are also other scenarios which are not covered directly by this parser. It is supposed to be small and easy to use. I fear, including more functions and features would make it too complicated.
However, there is an easy solution for your problem:
Create one instance of CLAParser to find out the context. Like:
CmdLine.Parameter(CLAParser.CLAParser.ParamAllowType.Optional, "ACCOUNTS", CLAParser.CLAParser.ValueType.Bool, "help text...");
CmdLine.Parameter(CLAParser.CLAParser.ParamAllowType.Optional, "COMPUTER ", CLAParser.CLAParser.ValueType.Bool, "help text...");
CmdLine.AllowAdditionalParameters = true;
...
Then create for each context one other instance of CLAParser. Like:
if(CmdLine["ACCOUNTS"] != null)
{
CLAParser.CLAParser CmdLineACCOUNTS = new CLAParser.CLAParser("CLAParserTest");
CmdLineACCOUNTS.AllowAdditionalParameters = false;
CmdLineACCOUNTS.Parse(CmdLine.GetAdditionalParameters().ToArray());
} else if(CmdLine["COMPUTER"])
{
CLAParser.CLAParser CmdLineCOMPUTER = new CLAParser.CLAParser("CLAParserTest");
CmdLineCOMPUTER.AllowAdditionalParameters = false;
CmdLineCOMPUTER.Parse(CmdLine.GetAdditionalParameters().ToArray());
} else
{
Console.WriteLine(CmdLine.GetUsage());
}
modified on Wednesday, April 7, 2010 9:28 AM
modified 24-Aug-17 8:34am.
|
|
|
|
|
Have a look at
Check out : http://niagara.codeplex.com/SourceControl/changeset/view/31682#691364 / NiagaraDataServiceUtil folder
Still I like what you have done, and can totally see this being useful, so have made your article public and voted it a 5Sacha Barber
- Microsoft Visual C# MVP 2008/2009
- Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
My Blog : sachabarber.net
|
|
|
|
|
Thanks for your reply! I think I even stumbled over that one before. However it lacks verification of the passed and parsed arguments. At least I do not see how to define which parameters are optional and which are required. That is why I wanted to make my own (for my feeling) more intuitive parser.
|
|
|
|