Friday, February 4, 2011

What is the design pattern for processing command line arguments

If you are writing a program that is executable from the command line, you often want to offer the user several options or flags, along with possibly more than one argument. I have stumbled my way through this many times, but is there some sort of design pattern for looping through args and spinning off the appropriate functions?

Consider:

myprogram -f filename -d directory -r regex

How do you organize the code after you retrieve the arguments using whatever built-ins for your language? (language-specific answers welcomed, if that helps you articulate an answer)

  • I don't know of any documented "patterns" for processing.

    I believe one of the oldest libraries/APIs for handling arguments is getopt. Googling "getopt" shows lots of man pages and links to implementations.

    Generally, I have a preferences or settings service in my application that the argument processor knows how to communicate with. Arguments are then translated into something in this service that the application than then query. This could be as simple as a dictionary of settings (like a string setting named "filename").

  • I would recommend using a command line processor library. Some Russian guy created a decent one, but there are tons of them out there. Will save you some time so you can concentrate on the purpose of your app rather than parsing command line switches!

    From Kilhoffer
  • You didn't mention the language, but for Java we've loved Apache Commons CLI. For C/C++, getopt.

  • You don't mention a language for this but if you are looking for a really nice Objective-C wrapper around getopt then Dave Dribin's DDCLI framework is really nice.

    http://www.dribin.org/dave/blog/archives/2008/04/29/ddcli

  • I use the Getopts::std and Getopts::long in perl and also the Getopt function in C. This standardises the parsing and format of parameters. Other languages have different mechanisms for handling these.

    Hope this helps

    From Xetius
  • The standard design usually follows what getopt does, there are getopt libraries for many languages, .NET, python, C, Perl, PHP, etc.

    The basic design is to have a command line parser which returns part by part the arguments passed to be checked in a loop.

    This article discusses it in some more detail.

  • Getopt is the only way to go.

    http://sourceforge.net/projects/csharpoptparse

    From cfeduke
  • The boost::program_options library is nice if you're in C++ and have the luxury of using Boost.

    From argv0
  • I am not as much interested in the libraries, though that is definitely helpful. I was looking more for some "pseudo code" that illustrates the processing of say your average bunch of flags and a bunch of longer arguments, as an example.

    ragu.pattabi : You might want to update this in your question rather than here about clarification. It is only meant for answers.
    From Sam McAfee
  • Assuming you have a "config" object that you aim to setup with the flags and a suitable command line parser that takes care of parsing the command line and supply a constant stream of the options, here goes a block of pseudocode

    while (current_argument = cli_parser_next()) {
        switch(current_argument) {
            case "f": //Parser strips the dashes
            case "force":
                config->force = true;
                break;
            case "d":
            case "delete":
                config->delete = true;
                break;
            //So on and so forth
            default:
                printUsage();
                exit;
        }
    }
    
  • I prefer options like "-t text" and "-i 44"; I don't like "-fname" or "--very-long-argument=some_value".

    And "-?", "-h", and "/h" all produce a help screen.

    Here's how my code looks:

    int main (int argc, char *argv[])
       {  int i;
          char *Arg;
          int ParamX, ParamY;
          char *Text, *Primary;
    
       // Initialize...
       ParamX = 1;
       ParamY = 0;
       Text = NULL;
       Primary = NULL;
    
       // For each argument...
       for (i = 0; i < argc; i++)
          {
          // Get the next argument and see what it is
          Arg = argv[i];
          switch (Arg[0])
             {
             case '-':
             case '/':
                // It's an argument; which one?
                switch (Arg[1])
                   {
                   case '?':
                   case 'h':
                   case 'H':
                      // A cry for help
                      printf ("Usage:  whatever...\n\n");
                      return (0);
                      break;
    
                   case 't':
                   case 'T':
                      // Param T requires a value; is it there?
                      i++;
                      if (i >= argc)
                         {
                         printf ("Error:  missing value after '%s'.\n\n", Arg);
                         return (1);
                         }
    
                      // Just remember this
                      Text = Arg;
    
                      break;
    
                   case 'x':
                   case 'X':
                      // Param X requires a value; is it there?
                      i++;
                      if (i >= argc)
                         {
                         printf ("Error:  missing value after '%s'.\n\n", Arg);
                         return (1);
                         }
    
                      // The value is there; get it and convert it to an int (1..10)
                      Arg = argv[i];
                      ParamX = atoi (Arg);
                      if ((ParamX == 0) || (ParamX > 10))
                         {
                         printf ("Error:  invalid value for '%s'; must be between 1 and 10.\n\n", Arg);
                         return (1);
                         }
    
                      break;
    
                   case 'y':
                   case 'Y':
                      // Param Y doesn't expect a value after it
                      ParamY = 1;
                      break;
    
                   default:
                      // Unexpected argument
                      printf ("Error:  unexpected parameter '%s'; type 'command -?' for help.\n\n", Arg);
                      return (1);
                      break;
                   }
    
                break;
    
             default:
                // It's not a switch that begins with '-' or '/', so it's the primary option
                Primary = Arg;
    
                break;
             }
          }
    
       // Done
       return (0);
       }
    
  • A few comments on this...

    First, while there aren't any patterns per se, writing a parser is essentially a mechanical exercise, since given a grammar, a parser can be easily generated. Tools like Bison, and ANTLR come to mind.

    That said, parser generators are usually overkill for the command line. So the usual pattern is to write one yourself (as others have demonstrated) a few times until you get sick of dealing with the tedious detail and find a library to do it for you.

    I wrote one for C++ that saves a bunch of effort that getopt imparts and makes nice use of templates: TCLAP

    From mes5k
  • I'm riffing on the ANTLR answer by mes5k. This link to Codeproject is for an article that discusses ANLTR and using the visit pattern to implement the actions you want you app to take. It's well written and worth reviewing.

0 comments:

Post a Comment