When building console applications that take parameters, you can use the arguments passed to Main(string[] args)
.
In the past I've simply indexed/looped that array and done a few regular expressions to extract the values. However, when the commands get more complicated, the parsing can get pretty ugly.
So I'm interested in:
- Libraries that you use
- Patterns that you use
Assume the commands always adhere to common standards such as answered here .
Source: Tips4all
I would strongly suggest using NDesk.Options (Documentation) and/or Mono.Options (same API, different namespace). An example from the documentation:
ReplyDeletebool show_help = false;
List<string> names = new List<string> ();
int repeat = 1;
var p = new OptionSet () {
{ "n|name=", "the {NAME} of someone to greet.",
v => names.Add (v) },
{ "r|repeat=",
"the number of {TIMES} to repeat the greeting.\n" +
"this must be an integer.",
(int v) => repeat = v },
{ "v", "increase debug message verbosity",
v => { if (v != null) ++verbosity; } },
{ "h|help", "show this message and exit",
v => show_help = v != null },
};
List<string> extra;
try {
extra = p.Parse (args);
}
catch (OptionException e) {
Console.Write ("greet: ");
Console.WriteLine (e.Message);
Console.WriteLine ("Try `greet --help' for more information.");
return;
}
The WPF TestApi library comes with one of the nicest command line parsers for C# development. I highly recommend looking into it, from Ivo Manolov's blog on the API:
ReplyDelete// EXAMPLE #2:
// Sample for parsing the following command-line:
// Test.exe /verbose /runId=10
// This sample declares a class in which the strongly-
// typed arguments are populated
public class CommandLineArguments
{
bool? Verbose { get; set; }
int? RunId { get; set; }
}
CommandLineArguments a = new CommandLineArguments();
CommandLineParser.ParseArguments(args, a);
I really like the Command Line Parser Library ( http://commandline.codeplex.com/ ). It has a very simple and elegant way of setting up parameters via attributes:
ReplyDeleteclass Options
{
[Option("i", "input", Required = true, HelpText = "Input file to read.")]
public string InputFile = null;
[Option(null, "length", HelpText = "The maximum number of bytes to process.")]
public int MaximumLenght = -1;
[Option("v", null, HelpText = "Print details during execution.")]
public bool Verbose = false;
[HelpOption(HelpText = "Dispaly this help screen.")]
public string GetUsage()
{
var usage = new StringBuilder();
usage.AppendLine("Quickstart Application 1.0");
usage.AppendLine("Read user manual for usage instructions...");
return usage.ToString();
}
}
Look at http://github.com/mono/mono/tree/master/mcs/class/Mono.Options/
ReplyDeleteI wrote a C# command line argument parser a while back. Its at: http://www.codeplex.com/CommandLineArguments
ReplyDeleteA previous discussion, split-string-containing-command-line-parameters-into-string-in-c#, may have some answers.
ReplyDeleteLooks like everybody has their own pet command-line parsers, figure I had better add mine as well :).
ReplyDeletehttp://bizark.codeplex.com/
This library contains a command-line parser that will initialize a class with the values from the command-line. It has a ton of features (I've been building it up over many years).
From the documentation...
Command-line parsing in the BizArk framework has these key features:
Automatic initialization: Class properties are automatically set based on the command-line arguments.
Default properties: Send in a value without specifying the property name.
Value conversion: Uses the powerful ConvertEx class also included in BizArk to convert values to the proper type.
Boolean flags: Flags can be specified by simply using the argument (ex, /b for true and /b- for false) or by adding the value true/false, yes/no, etc.
Argument arrays: Simply add multiple values after the command-line name to set a property that is defined as an array. Ex, /x 1 2 3 will populate x with the array { 1, 2, 3 } (assuming x is defined as an array of integers).
Command-line aliases: A property can support multiple command-line aliases for it. For example, Help uses the alias ?.
Partial name recognition: You don’t need to spell out the full name or alias, just spell enough for the parser to disambiguate the property/alias from the others.
Supports ClickOnce: Can initialize properties even when they are specified as the query string in a URL for ClickOnce deployed applications. The command-line initialization method will detect if it is running as ClickOnce or not so your code doesn’t need to change when using it.
Automatically creates /? help: This includes nice formatting that takes into account the width of the console.
Load/Save command-line arguments to a file: This is especially useful if you have multiple large, complex sets of command-line arguments that you want to run multiple times.
I like that one, because you can "define rules" for the arguments, needed or not,...
ReplyDeleteor if you're a Unix guy, than you might like the GNU Getopt .NET port.
There are numerous solutions to this problem. For completeness and to provide the alternative if someone desires I'm adding this answer for two useful classes in my google code library.
ReplyDeleteThe first is ArgumentList which is responsible only for parsing command line parameters. It collects name-value pairs defined by switches '/x:y' or '-x=y' and also collects a list of 'unnamed' entries. It's basic usage is discussed here, view the class here.
The second part of this is the CommandInterpreter which creates a fully-functional command-line application out of your .Net class. As an example:
using CSharpTest.Net.Commands;
static class Program
{
static void Main(string[] args)
{
new CommandInterpreter(new Commands()).Run(args);
}
//example ‘Commands’ class:
class Commands
{
public int SomeValue { get; set; }
public void DoSomething(string svalue, int ivalue)
{ ... }
With the above example code you can run the following:
Program.exe DoSomething "string value" 5
-- or --
Program.exe dosomething /ivalue=5 -svalue:"string value"
It's as simple as that or as complex as you need it to be. You can review the source code, view the help, or download the binary.
There is a command line argument parser at http://www.codeplex.com/commonlibrarynet
ReplyDeleteIt can parse arguments using
1. attributes
2. explicit calls
3. single line of multiple arguments OR string array
It can handle things like the following:
-config:Qa -startdate:${today} -region:'New York' Settings01
It's very easy to use.
CLAP (command line argument parser) has a usable API and is wonderfully documented. You make a method, annotating the parameters. https://github.com/adrianaisemberg/CLAP
ReplyDeleteThis is a handler I wrote based on the Novell Options class.
ReplyDeleteThis one is aimed at console applications that execute a while (input !="exit") style loop, an interactive console such as an FTP console for example.
Example usage:
static void Main(string[] args)
{
// Setup
CommandHandler handler = new CommandHandler();
CommandOptions options = new CommandOptions();
// Add some commands. Use the v syntax for passing arguments
options.Add("show", handler.Show)
.Add("connect", v => handler.Connect(v))
.Add("dir", handler.Dir);
// Read lines
System.Console.Write(">");
string input = System.Console.ReadLine();
while (input != "quit" && input != "exit")
{
if (input == "cls" || input == "clear")
{
System.Console.Clear();
}
else
{
if (!string.IsNullOrEmpty(input))
{
if (options.Parse(input))
{
System.Console.WriteLine(handler.OutputMessage);
}
else
{
System.Console.WriteLine("I didn't understand that command");
}
}
}
System.Console.Write(">");
input = System.Console.ReadLine();
}
}
And the source:
/// <summary>
/// A class for parsing commands inside a tool. Based on Novell Options class (http://www.ndesk.org/Options).
/// </summary>
public class CommandOptions
{
private Dictionary<string, Action<string[]>> _actions;
private Dictionary<string, Action> _actionsNoParams;
/// <summary>
/// Initializes a new instance of the <see cref="CommandOptions"/> class.
/// </summary>
public CommandOptions()
{
_actions = new Dictionary<string, Action<string[]>>();
_actionsNoParams = new Dictionary<string, Action>();
}
/// <summary>
/// Adds a command option and an action to perform when the command is found.
/// </summary>
/// <param name="name">The name of the command.</param>
/// <param name="action">An action delegate</param>
/// <returns>The current CommandOptions instance.</returns>
public CommandOptions Add(string name, Action action)
{
_actionsNoParams.Add(name, action);
return this;
}
/// <summary>
/// Adds a command option and an action (with parameter) to perform when the command is found.
/// </summary>
/// <param name="name">The name of the command.</param>
/// <param name="action">An action delegate that has one parameter - string[] args.</param>
/// <returns>The current CommandOptions instance.</returns>
public CommandOptions Add(string name, Action<string[]> action)
{
_actions.Add(name, action);
return this;
}
/// <summary>
/// Parses the text command and calls any actions associated with the command.
/// </summary>
/// <param name="command">The text command, e.g "show databases"</param>
public bool Parse(string command)
{
if (command.IndexOf(" ") == -1)
{
// No params
foreach (string key in _actionsNoParams.Keys)
{
if (command == key)
{
_actionsNoParams[key].Invoke();
return true;
}
}
}
else
{
// Params
foreach (string key in _actions.Keys)
{
if (command.StartsWith(key) && command.Length > key.Length)
{
string options = command.Substring(key.Length);
options = options.Trim();
string[] parts = options.Split(' ');
_actions[key].Invoke(parts);
return true;
}
}
}
return false;
}
}
You may like my one Rug.Cmd
ReplyDeleteEasy to use and expandable command line argument parser. Handles: Bool, Plus / Minus, String, String List, CSV, Enumeration.
Built in '/?' help mode.
Built in '/??' and '/?D' document generator modes.
static void Main(string[] args)
{
// create the argument parser
ArgumentParser parser = new ArgumentParser("ArgumentExample", "Example of argument parsing");
// create the argument for a string
StringArgument StringArg = new StringArgument("String", "Example string argument", "This argument demonstrates string arguments");
// add the argument to the parser
parser.Add("/", "String", StringArg);
// parse arguemnts
parser.Parse(args);
// did the parser detect a /? argument
if (parser.HelpMode == false)
{
// was the string argument defined
if (StringArg.Defined == true)
{
// write its value
RC.WriteLine("String argument was defined");
RC.WriteLine(StringArg.Value);
}
}
}
Edit: This is my project and as such this answer should not be seen as an endorsement from a third party. That said I do use it for every command line based program I write, it is open source and it is my hope that others may benefit from it.
Genghis Command Line Parser may be a little out of date, but it is very feature complete and works pretty well for me.
ReplyDeleteI recently came across The FubuCore Command line parsing implementation I really like it, the reasons being:
ReplyDeleteit's easy to use - although I couldn't find a documentation for it, the FubuCore solution also provides a project containing a nice set of Unit Tests that speak more about the functionality than any documentation could
it has a nice object oriented design, no code repetition or other such things that I used to have in my command line parsing apps
it's declarative: you basically write classes for the Commands and sets of parameters and decorate them with attributes to set various options (e.g. name, description, mandatory/optional)
the library even prints a nice Usage Graph, based on these definitions
Below is a simple example on how to use this. To illustrate the usage, I've written a simple utility that has two commands:
- add (adds an object to a list - an object consists of a name(string), value(int) and a boolean flag)
- list (lists all the currently added objects)
First of all, I wrote a Command class for the 'add' command:
[Usage("add", "Adds an object to the list")]
[CommandDescription("Add object", Name = "add")]
public class AddCommand : FubuCommand<CommandInput>
{
public override bool Execute(CommandInput input)
{
State.Objects.Add(input); // add the new object to an in-memory collection
return true;
}
}
This command takes a CommandInput instance as parameter, so I define that next:
public class CommandInput
{
[RequiredUsage("add"), Description("The name of the object to add")]
public string ObjectName { get; set; }
[ValidUsage("add")]
[Description("The value of the object to add")]
public int ObjectValue { get; set; }
[Description("Multiply the value by -1")]
[ValidUsage("add")]
[FlagAlias("nv")]
public bool NegateValueFlag { get; set; }
}
The next command is 'list', which is implemented as follows:
[Usage("list", "List the objects we have so far")]
[CommandDescription("List objects", Name = "list")]
public class ListCommand : FubuCommand<NullInput>
{
public override bool Execute(NullInput input)
{
State.Objects.ForEach(Console.WriteLine);
return false;
}
}
The 'list' command takes no parameters, so I defined a NullInput class for this:
public class NullInput { }
All that's left now is to wire this up in the Main() method, like this:
static void Main(string[] args)
{
var factory = new CommandFactory();
factory.RegisterCommands(typeof(Program).Assembly);
var executor = new CommandExecutor(factory);
executor.Execute(args);
}
The program works as expected, printing hints about the correct usage in case any commands are invalid:
------------------------
Available commands:
------------------------
add -> Add object
list -> List objects
------------------------
And a sample usage for the 'add' command:
Usages for 'add' (Add object)
add <objectname> [-nv]
-------------------------------------------------
Arguments
-------------------------------------------------
objectname -> The name of the object to add
objectvalue -> The value of the object to add
-------------------------------------------------
-------------------------------------
Flags
-------------------------------------
[-nv] -> Multiply the value by -1
-------------------------------------
I would suggest the open-source library CSharpOptParse. It parses the command line and hydrates a user-defined .NET object with the command-line input. I always turn to this library when writing a C# console application.
ReplyDeleteMy personal favorite is http://www.codeproject.com/KB/recipes/plossum_commandline.aspx by Peter Palotas:
ReplyDelete[CommandLineManager(ApplicationName="Hello World",
Copyright="Copyright (c) Peter Palotas")]
class Options
{
[CommandLineOption(Description="Displays this help text")]
public bool Help = false;
[CommandLineOption(Description = "Specifies the input file", MinOccurs=1)]
public string Name
{
get { return mName; }
set
{
if (String.IsNullOrEmpty(value))
throw new InvalidOptionValueException(
"The name must not be empty", false);
mName = value;
}
}
private string mName;
}
Please use the .net port of the apache commons cli API. This works great.
ReplyDeletehttp://sourceforge.net/projects/dotnetcli/
and the original API for concepts and introduction
http://commons.apache.org/cli/
A very simple easy to use ad hoc class for command line parsing, that supports default arguments.
ReplyDeleteclass CommandLineArgs
{
public static CommandLineArgs I
{
get
{
return m_instance;
}
}
public string argAsString( string argName )
{
if (m_args.ContainsKey(argName)) {
return m_args[argName];
}
else return "";
}
public long argAsLong(string argName)
{
if (m_args.ContainsKey(argName))
{
return Convert.ToInt64(m_args[argName]);
}
else return 0;
}
public double argAsDouble(string argName)
{
if (m_args.ContainsKey(argName))
{
return Convert.ToDouble(m_args[argName]);
}
else return 0;
}
public void parseArgs(string[] args, string defaultArgs )
{
m_args = new Dictionary<string, string>();
parseDefaults(defaultArgs );
foreach (string arg in args)
{
string[] words = arg.Split('=');
m_args[words[0]] = words[1];
}
}
private void parseDefaults(string defaultArgs )
{
if ( defaultArgs == "" ) return;
string[] args = defaultArgs.Split(';');
foreach (string arg in args)
{
string[] words = arg.Split('=');
m_args[words[0]] = words[1];
}
}
private Dictionary<string, string> m_args = null;
static readonly CommandLineArgs m_instance = new CommandLineArgs();
}
class Program
{
static void Main(string[] args)
{
CommandLineArgs.I.parseArgs(args, "myStringArg=defaultVal;someLong=12");
Console.WriteLine("Arg myStringArg : '{0}' ", CommandLineArgs.I.argAsString("myStringArg"));
Console.WriteLine("Arg someLong : '{0}' ", CommandLineArgs.I.argAsLong("someLong"));
}
}
Here is another option on the Google Code Library:
ReplyDeleteGoogle Code
It uses reflection to populate a data class from the input command line args.