For many years (more than I care to remember) I have continually written code to provide command-line behavior over and over again. On occasion I would get fancy in one project or another and build something sorta-generic; however, every time it came to creating the next command-line tool I started from scratch. Finally I said “NO MORE!”

The result of that was the CommandInterpreter class. What does it do? Well you give it a System.Type and it will create ‘commands’ from all public static methods, or give it an object instance and it will create ‘commands’ from all the public instance methods. With these commands you can have a fully operational command-line application complete with help in one line of code:

	using CSharpTest.Net.Commands;
	static class Program
	{
		static void Main(string[] args)
		{
			new CommandInterpreter(typeof(Commands)).Run(args);
		}
	}

For this example let’s pretend we have the following ‘Commands’ class:

	static class Commands
	{
		public static void DoSomething(string whoAreYou, int repeatMessage)
		{
			for (int i = 0; i < repeatMessage; i++)
				Console.WriteLine("Hello {0}, how are you today?", whoAreYou);
		}
	}

Now let’s see what we can do:

C:\Test>Sample.exe help
Commands:
  DOSOMETHING:  Some descriptive text
      HELP:  Gets the help for a specific command or lists available commands.

C:\Test>Sample.exe help dosomething

DOSOMETHING [/whoAreYou=]String [/repeatMessage=]Int32

descriptive text

Arguments:

  [/whoAreYou=]String argument description
  [/repeatMessage=]String argument description

C:\Test>Sample.exe dosomething roger 2
Hello roger, how are you today?
Hello roger, how are you today?

 
So this is where your jorney begins and mine ends, there is so much more you can do with this class I can’t put it all here … even though I will still try:

  • Run interactively with the console via CommandInterpreter.Run(Console.In)
  • Use global environment settings by using public properties on the object/type.
  • Inject properties into arguments via nmake-like $(OptionName) macro expansion.
  • Implement a command as a interface (ICommand, IOption) and do anything.
  • Imlement custom argument parsing by declaring yourMethod([AllArguments]string[] args)
  • Environment.ExitCode is set for you on exceptions, no need to catch{} anything.
  • Throw an ApplicationException to output to Console.Error only the exception’s message.
  • Run complete scripts via Run(TextReader in).
  • Aggregate commands by accepting an ICommandInterpreter and calling Run(…).
  • Inject an ICommandFilter implementation to pre/post process every command.
  • Provide arguments by ordinal occurrence or explicitly by /name=value or -name:value
  • Accept multiple values for an argument by using (string[] name)

 

Attributes used:
  • [System.ComponentModel.DisplayName("x")] – renames a command.
  • [System.ComponentModel.Description("x")] – defines help for methods and arguments.
  • [System.ComponentModel.Browsable(false)] – hides a methodfrom the help command.
  • [System.ComponentModel.DefaultValue(0)] – default value for a property or argument.
  • [AliasName("x")] – alternative name(s) for commands, options, and arguments.
  • [AllArguments] string[] – argument receives all original arguments.
  • Declare a method as an ICommandFilter with the [CommandFilter] attribute.

 

I know your interested, so go get the source code or download the binaries.
Comments