Click here to Skip to main content
15,884,629 members
Articles / Programming Languages / C#

Commander - A Command Parser

Rate me:
Please Sign up or sign in to vote.
4.75/5 (3 votes)
13 Feb 2012CPOL3 min read 17.4K   466   14  
Expandable Command Line Parser

Introduction

Although a graphical user interface (GUI) is more common those days, I found that the command-line interface (CLI) is better way to interact with a software to perform specific tasks. In this article I will show how to create expandable command-line interpreter that receives, parse, and executes commands.

Background

When designing the interpreter the following principles was followed :

  • Simplify adding or removing commands.
  • Allow to reuse commands in different programs and contexts
  • Allow to group commands and use them in different programs and contexts
  • Show list of available commands
  • Show detailed help about specific command

The Demo Program

public class Demo {
  public static int Main(string[] sargs) {

    Commander countCmd = new Commander("count",2,1);
    countCmd.Add(new CountCommand());
    countCmd.Add(new CountDownCommand());

    Commander mainCmd = new Commander("demo",1,1);
    mainCmd.Add(new PrintCommand());
    mainCmd.Add(new HalloCommand()); 
    mainCmd.Add(countCmd);                

    return (int) mainCmd.Run(sargs);
  }
}

The added commands can be invoked by the demo program. Note that the help messages are based on the added commands.

E:\demo>demo help
demo Commands [version 1.01] :
  help       => Display available commands,'help CMD' for help on CMD
  print      => Print message count times
  hallo      => Print 'Hallo world!' count times
  count      => count Commands [version 2.01]. use count help for detail

E:\demo>demo help help
Usage:
  1. help
     * Display all available commands with summary
  2. help <command>

     * Display help on a specific <command>

E:\demo>demo help print
Usage: print --msg=<msg> --count=<count>

  * print <msg> <count> times

Arguments:

  * <msg>   - The message to print
  * <count> - How many times to print the message

E:\demo>demo print --msg=hallo --count=3
hallo
hallo
hallo
  
E:\demo>demo help hallo
Usage: hallo --count=<count>

  * print 'Hallo world!' <count> times

Arguments:

  * <count> - How many times to print 'Hallo world!'


E:\demo>demo hallo --count=3
Hallo world!
Hallo world!
Hallo world!
  
E:\demo>demo count help
count Commands [version 2.01] :
  help       => Display available commands,'help CMD' for help on CMD
  count      => Count up to the given <count>
  count-down => Count from <count> down to 1

E:\demo>demo count help count
Usage: count <count>

  * Count upto <count>

Arguments:
  * <count> - The maximum to count

E:\demo>demo count count 3
001
002
003
  
E:\demo>demo count help count-down
Usage: count-down <count>
  * ount from <count> down to 1

Arguments:
  * <count> - The statring number
  
E:\demo>demo count count-down 3
003
002
001

About the code

The ICommand Interface

The ICommand interface allowes to classes which implementes it to be invoked by a commander.

The RunStatus Run(CommandArgs args) is the key method in this interface. This method is responsible for executing the command's related code. The method is given CommandArgs object and should return the RunStatus of the command's execution. (Success, InvalidArgs, Error or NoCommand).

Each command have a Name, Help and Summary.
  • The Name should be a single word which used to identify the command.
  • The Summary should be a single line description about the command's purpose
  • The Help should be a detailed description about the command and its valid arguments

The CommandArgs

The arguments are given to command by CommandArgs object. There are two types of arguments that can be accessed using this object:

  • Named arguments are arguments that can be accessed by name and can have optional value. To access named argument the following methods can be used:
    • this[string name] Finds the value of named argument if exist, otherwise return null
    • bool HasArg(string name,out string value) Finds whether a named argument if exist and its value.
    • bool HasArg(string name) Finds whether a named argument if exist and its value.
  • Ordered arguments are arguments that can be accessed by index (1 based index) .To access named argument the following metdos can be used :
    • this[int index] Finds the index'th ordered argument.

The CommandName property holds the command's name while the Count property holds the number of ordered arguments.

Parsing from array of strings

The CommandArgs can be parsed from array of strings. To keep things simple the format is of named argument is --ARGNAME[=VALUE]. In other words , all strings that start with double dash (--) are treated as name argument. The argument name is delimited by -- and the end of the string or the optional equal sign (=). In the former case the value will of named argument will be empty string. in the latter case the value will be delimited by (=) and end of string.

The first string does not match this format will be treated as the command's name. Any other string will be treated as ordered argument.

The following code should clarify how the parsing is done

CommandArgs args = new CommandArgs(new string[] {
  "show","--count=10","--display","arg1","arg2","arg3"
});
System.Console.WriteLine(args.CommandName);       // show
System.Console.WriteLine(args["count"]);          // 10
System.Console.WriteLine(args["display"] == "");  // True
System.Console.WriteLine(args.Count);             // 3
System.Console.WriteLine(args[1]);                // arg1
System.Console.WriteLine(args[2]);                // arg2
System.Console.WriteLine(args[3]);                // arg3
System.Console.WriteLine(args["none"] == null);   // True
System.Console.WriteLine(args[0] == null);        // True
System.Console.WriteLine(args[4] == null);        // True

The ICommander Interface

ICommander is special ICommand that can store list of commands, invoked them on demand. When the command is invoked using the Run(CommandArgs args) method. The commander invokes the command which matches args.CommandName.

To add a command to the commander we use the Add(ICommand cmd). As ICommander extends the ICommand interface, it can be also added to commander. This can be useful to chain and reuse ICommanders.

Each Commander has built-in help command HelpCommand. This command can be invoked without arguments or with command's name. In the former case the displays all available commands with summary. In the later case the a detailed help about this specific command.

History

  • Version 1 - First version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Israel Israel

I have been developing professionally since 2002.



I developed desktop applications in C++ and C#. Now developing web applications and sites in PHP.






Visit Panboza.

Comments and Discussions

 
-- There are no messages in this forum --