Discovering Command Line Options, Part IV

Some command line switches stand alone. Others are followed by options, such as a starting value, filename, and other settings. The getopt() function processes these values along with the switches, providing you know the secret.

Before revealing the secret (you can wait), know that a switch-case structure nested in a while loop is the best way to process multiple switches. The following listing shows how such a beast is assembled:

2021_07_17-Lesson-a.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int r;

    /* suppress getopt()'s error message */
    opterr = 0;

    while( (r=getopt(argc,argv,"az")) != -1 )
    {
        switch(r)
        {
            case 'a':
                puts("-a option is activated");
                /* do A option stuff here */
                break;
            case 'z':
                puts("-z option is activated");
                /* do Z option stuff here */
                break;
            case '?':
                printf("'%c' is an invalid argument\n",optopt);
                break;
            default:
                puts("Unknown argument");
        }
    }

    return(0);
}

The program accepts two arguments, -a and -z, as shown at Line 12. They can be specified in any order, as shown by these sample runs:

$ ./a.out -a -z
-a option is activated
-z option is activated
$ ./a.out -z -a
-z option is activated
-a option is activated

You can even bunch them up:

$ ./a.out -az
-a option is activated
-z option is activated
$ ./a.out -za
-z option is activated
-a option is activated

Things get tricky for the getopt() function when a switch requires an option. (This is the secret part.) For example:

$ ./a.out -a 177

Above, the -a switch requires an option, say the number of people who fully understand double pointers in C. To notify the getopt() function of such an option, suffix a colon to the switch’s character in the option string:

getopt(argc,argv,"a:z")

Above, the -a switch has an option but -z does not. The option is available immediately after the getopt() function reads the -a. It’s accessed as a string through the optarg global variable. So, when the option is a numeric value, you must convert it from a string to the proper data type.

The following code processes an argument for the -a switch, but not the -z switch:

2021_07_17-Lesson-b.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int r;

    /* suppress getopt()'s error message */
    opterr = 0;

    while( (r=getopt(argc,argv,"a:z")) != -1 )
    {
        switch(r)
        {
            case 'a':
                /* -a has an argument */
                printf("-a option with argument %s\n",optarg);
                /* do A option stuff here */
                break;
            case 'z':
                puts("-z option is activated");
                /* do Z option stuff here */
                break;
            case '?':
                printf("'%c' is an invalid argument\n",optopt);
                break;
            default:
                puts("Unknown argument");
        }
    }

    return(0);
}

In the case statements for option -a (starting at Line 16), the optarg variable is used in the printf() statement to output whatever argument follows -a. As with sample output shown previously, you can specify -a and -z in any order, but -a must have an argument, otherwise the optarg variable is misread. For example:

$ ./a.out -a -z
-a option with argument -z

It’s up to your code to determine whether the proper argument is set — or even available. Otherwise the switches are misinterpreted and their purpose is lost.

For multiple arguments with options, set a colon after each one in the getopt() function’s argument string. This operation can be tricky: You must test for each switch’s options. Otherwise a missing argument throws off the entire process.

In next week’s Lesson, I cover the getopt_long() function, which processes full-word switches prefixed by a double-dash.

Leave a Reply