Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I just ran into an odd scenario. I’m sure that I must have run into the same situation in the past and yet somehow not noticed it until now.

I am writing a program that requires at least two command-line arguments. Dealing with this is simple enough, just check that there are sufficient arguments. For example:

A:  if (argc < 3) PrintHelpAndQuit();

However, I also want to allow the user to specifically request the help-screen(s) using a single command-line argument. For example:

B:  if (args[1] == "/?")  PrintHelpAndQuit();
C:  if (args[1] == "/?1") PrintHelp1AndQuit();
D:  if (args[1] == "/?2") PrintHelp2AndQuit();


The problem is that if I put line A first, it ensures that at least two arguments were specified, which prevents then lines B, C, and D from being called, and so the user cannot access those screens.

If I put line A after the others, then the program will crash if no arguments are specified (accessing the first member of the argument array is undefined).


There seems to be (only?) two ways to approach this:

  • Put the help-screen lines in a conditional block that checks that there is at least one argument (which duplicates the work of line A):

    if (argc > 1) {
      if (args[1] == "/?...
    }
    
    if (argc < 3) PrintHelpAndQuit();
    


  • Put an extra line at the top that checks if there are no arguments and prints help and quits if so:

    if (argc < 2) PrintHelpAndQuit(); // now almost entirely duplicates line A
    if (args[1] == "/?...
    if (argc < 3) PrintHelpAndQuit();
    


Both of these approaches are redundant to varying degrees.


How do others deal with this situation? Is there a more elegant (and less redundant) way to require a minimum number of arguments without precluding a single argument?

share|improve this question
2  
Using one of the available command line argument parsers (getopts(1) and getopt(3) on POSIX-y systems, Getopt::Long in Perl, Apache Commons CLI for Java, etc.) would probably make your life a lot easier. –  Blrfl Jun 11 '13 at 14:13
add comment

4 Answers

up vote 3 down vote accepted

Logically, the problem as defined requires a check that there is at least one argument for the help case, as well as a check that there are at least two arguments for the normal use case.

Think about what you have to say to describe the program's behavior:

[The program] requires at least two command-line arguments...

Then

I also want to allow the user to specifically request the help-screen(s) using a single command-line argument.

Even to describe the behavior in words, you need two different requirements about the number of arguments. It is no surprise that the code similarly needs two requirements. Having two checks is not redundant, because it is the minimum amount needed to logically test for the valid options.

share|improve this answer
    
I think this makes sense. I was looking through a couple of my other programs to see how I handled arguments in them and noticed that they often handle /h in an arg-processing loop (e.g., for (int i=1; i<argc; i++) {…}) . In essence the loop itself (well, the condition section of it) is basically reproducing the check for at least one argument. –  Synetech Jun 11 '13 at 16:51
add comment

Command line utilities, in my experience, tend to be universally ugly when it comes to being flexible when parsing args, especially when you have a command that can take an arbitrary number of arguments. There will almost always be some level of duplication or cross-checking, because at its heart the tool is likely short, which means it's probably a single file, which means it's likely procedural.

The first step to any command-line tool is parsing the arguments. This is basically the equivalent of setting up the initial state of the state machine represented by the rest of your script.

Here, have some pseudocode:

if args.length is 0 or (args.length is 1 and args[0] is '--help')
    do_help_message_thing();
elseif args.length is 1
    do_invalid_usage_thing();

I would not worry too much about the code in your tool. You're writing the tool to encapsulate a bit of unenjoyable misery so that everyone who uses the tool in the future can take a shortcut. As long as the script is not buggy, relatively fast, and doesn't have the potential to hose a system, people will love you for it (in my experience, at least).

share|improve this answer
    
I disagree, I find that they are often very flexible, at least outside of windows, because command line parsing has been a solved problem for a long time now and there are plenty of excellent libraries available for it. –  whatsisname Jun 11 '13 at 15:58
add comment

In *nix tools it is common to require that all options/flags are positioned before mandatory arguments. With that requirement and the fact that you just discard the rest of the arguments if you start a help function you can do the following:

switch args[1]:
    case "/?") PrintHelpAndQuit();
    case "/?1") PrintHelp1AndQuit();
    case "/?2") PrintHelp2AndQuit();

Once you get past the switch statement you know that any following arguments are the (at least two) necessary arguments, and you can check argc < 3.

share|improve this answer
    
The switch is just another way to check for at least one argument (another way of writing if (narg>=1) {…}). –  Synetech Jun 11 '13 at 16:54
add comment

Don't work with argument counts. Place the arguments into a queue and process the queue. This is a lot easier to work with.

Queue q = new Queue();
for(int i=0; i < argc; i++)
{
    q.push(args[i]);
}

Once their in the queue you can then process the arguments via a simple dispatch method. What you should do is follow a consistent pattern for the platform. On Windows arguments are prefixed with a forward slash, and arguments without a slash are parameters for the previous argument. On Linux it's a double dash.

Let's assume we have a few usages like this

tool.exe /mode on /prompt off
tooo.exe /? /mode on

The first would set some options, and the second would display help for an option. Since you have the arguments in a queue you can process it like this.

while(!q.empty())
{
    String arg = q.pop();
    // is it an option
    if(arg.startsWith("/"))
    {
        switch(arg)
        {
            case "/mode":
               setMode(q.pop());
               break;
            case "/prompt":
               setPrompt(q.pop());
               break;
            case "/help":
               showHelp(q.pop(),q.pop());
               exit();
               break;
        }
    }
    else
    {
       exit("Unexpected argument");
    }
}

// run the program, all options are set

Now I didn't do any checks on the queue missing arguments, but you could do your own queue where pop() accepted a default value.

With a queue the order of arguments doesn't break anything.

tool.exe /mode on /prompt off
tool.exe /prompt off /mode on
tooo.exe /prompt on /? /mode on

All the above are valid.

share|improve this answer
    
Actually, this is very similar to what I just wrote about. The loop is in essence another form of if (nargs>=1). –  Synetech Jun 11 '13 at 16:54
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.