Home > Articles

  • Print
  • + Share This
This chapter is from the book

2.3 Option Parsing: getopt() and getopt_long()

Circa 1980, for System III, the Unix Support Group within AT&T noted that each Unix program used ad hoc techniques for parsing arguments. To make things easier for users and developers, they developed most of the conventions we listed earlier. (The statement in the System III intro(1) manpage is considerably less formal than what's in the POSIX standard, though.)

The Unix Support Group also developed the getopt() function, along with several external variables, to make it easy to write code that follows the standard conventions. The GNU getopt_long() function supplies a compatible version of getopt(), as well as making it easy to parse long options of the form described earlier.

2.3.1 Single-Letter Options

The getopt() function is declared as follows:

#include <unistd.h>                                              POSIX
   
   int getopt(int argc, char *const argv[], const char *optstring);
   
   extern char *optarg;
   extern int optind, opterr, optopt;
   

The arguments argc and argv are normally passed straight from those of main(). optstring is a string of option letters. If any letter in the string is followed by a colon, then that option is expected to have an argument.

To use getopt(), call it repeatedly from a while loop until it returns -1. Each time that it finds a valid option letter, it returns that letter. If the option takes an argument, optarg is set to point to it. Consider a program that accepts a -a option that doesn't take an argument and a -b argument that does:

int oc;             /* option character */
char *b_opt_arg;

while ((oc = getopt(argc, argv, "ab:")) != -1) {
    switch (oc) {
    case 'a':
        /* handle -a, set a flag, whatever */
        break;
    case 'b':
        /* handle -b, get arg value from optarg */
        b_opt_arg = optarg;
        break;
    case ':':
        ...       /* error handling, see text */
    case '?':
    default:
        ...       /* error handling, see text */
    }
}

As it works, getopt() sets several variables that control error handling.

char *optarg

  • The argument for an option, if the option accepts one.

int optind

  • The current index in argv. When the while loop has finished, remaining operands are found in argv[optind] through argv[argc-1]. (Remember that 'argv[argc] == NULL'.)

int opterr

  • When this variable is nonzero (which it is by default), getopt() prints its own error messages for invalid options and for missing option arguments.

int optopt

  • When an invalid option character is found, getopt() returns either a '?' or a ':' (see below), and optopt contains the invalid character that was found.

People being human, it is inevitable that programs will be invoked incorrectly, either with an invalid option or with a missing option argument. In the normal case, getopt() prints its own messages for these cases and returns the '?' character. However, you can change its behavior in two ways.

First, by setting opterr to 0 before invoking getopt(), you can force getopt() to remain silent when it finds a problem.

Second, if the first character in the optstring argument is a colon, then getopt() is silent and it returns a different character depending upon the error, as follows:

Invalid option

  • getopt() returns a '?' and optopt contains the invalid option character. (This is the normal behavior.)

Missing option argument

  • getopt() returns a ':'. If the first character of optstring is not a colon, then getopt() returns a '?', making this case indistinguishable from the invalid option case.

Thus, making the first character of optstring a colon is a good idea since it allows you to distinguish between "invalid option" and "missing option argument." The cost is that using the colon also silences getopt(), forcing you to supply your own error messages. Here is the previous example, this time with error message handling:

int oc;             /* option character */
char *b_opt_arg;

while ((oc = getopt(argc, argv, ":ab:")) != -1) {
    switch (oc) {
    case 'a':
        /* handle -a, set a flag, whatever */
        break;
    case 'b':
        /* handle -b, get arg value from optarg */
        b_opt_arg = optarg;
        break;
    case ':':
        /* missing option argument */
        fprintf(stderr, "%s: option '-%c' requires an argument\n",
                argv[0], optopt);
        break;
    case '?':
    default:
        /* invalid option */
        fprintf(stderr, "%s: option '-%c' is invalid: ignored\n",
                argv[0], optopt);
        break;
    }
}

A word about flag or option variable-naming conventions: Much Unix code uses names of the form xflg for any given option letter x (for example, nflg in the V7 echo; xflag is also common). This may be great for the program's author, who happens to know what the x option does without having to check the documentation. But it's unkind to someone else trying to read the code who doesn't know the meaning of all the option letters by heart. It is much better to use names that convey the option's meaning, such as no_newline for echo's -n option.

2.3.2 GNU getopt() and Option Ordering

The standard getopt() function stops looking for options as soon as it encounters a command-line argument that doesn't start with a '-'. GNU getopt() is different: It scans the entire command line looking for options. As it goes along, it permutes (rearranges) the elements of argv, so that when it's done, all the options have been moved to the front and code that proceeds to examine argv[optind] through argv[argc-1] works correctly. In all cases, the special argument '--' terminates option scanning.

You can change the default behavior by using a special first character in optstring, as follows:

optstring[0] == '+'

  • GNU getopt() behaves like standard getopt(); it returns options in the order in which they are found, stopping at the first nonoption argument. This will also be true if POSIXLY_CORRECT exists in the environment.

optstring[0] == '-'

  • GNU getopt() returns every command-line argument, whether or not it represents an argument. In this case, for each such argument, the function returns the integer 1 and sets optarg to point to the string.

As for standard getopt(), if the first character of optstring is a ':', then GNU getopt() distinguishes between "invalid option" and "missing option argument" by returning '?' or ':', respectively. The ':' in optstring can be the second character if the first character is '+' or '-'.

Finally, if an option letter in optstring is followed by two colon characters, then that option is allowed to have an optional option argument. (Say that three times fast!) Such an argument is deemed to be present if it's in the same argv element as the option, and absent otherwise. In the case that it's absent, GNU getopt() returns the option letter and sets optarg to NULL. For example, given—

while ((c = getopt(argc, argv, "ab::")) != 1)
   ...

—for -bYANKEES, the return value is 'b', and optarg points to "YANKEES", while for -b or '-b YANKEES', the return value is still 'b' but optarg is set to NULL. In the latter case, "YANKEES" is a separate command-line argument.

2.3.3 Long Options

The getopt_long() function handles the parsing of long options of the form described earlier. An additional routine, getopt_long_only() works identically, but it is used for programs where all options are long and options begin with a single '-' character. Otherwise, both work just like the simpler GNU getopt() function. (For brevity, whenever we say "getopt_long()," it's as if we'd said "getopt_long() and getopt_long_only().") Here are the declarations, from the GNU/Linux getopt(3) manpage:

#include <getopt.h>                                                GLIBC
   
   int getopt_long(int argc, char *const argv[],
   const char *optstring,
   const struct option *longopts, int *longindex);
   
   int getopt_long_only(int argc, char *const argv[],
   const char *optstring,
   const struct option *longopts, int *longindex);
   

The first three arguments are the same as for getopt(). The next option is a pointer to an array of struct option, which we refer to as the long options table and which is described shortly. The longindex parameter, if not set to NULL, points to a variable which is filled in with the index in longopts of the long option that was found. This is useful for error diagnostics, for example.

2.3.3.1 Long Options Table

Long options are described with an array of struct option structures. The struct option is declared in <getopt.h>; it looks like this:

struct option {
    const char *name;
    int has_arg;
    int *flag;
    int val;
};

The elements in the structure are as follows:

const char *name

  • This is the name of the option, without any leading dashes, for example, "help" or "verbose".

int has_arg

  • This describes whether the long option has an argument, and if so, what kind of argument. The value must be one of those presented in Table 2.1.

    Table 2.1. Values for has_arg

    Symbolic constant

    Numeric value

    Meaning

    no_argument

    0

    The option does not take an argument.

    required_argument

    1

    The option requires an argument.

    optional_argument

    2

    The option's argument is optional.

    The symbolic constants are macros for the numeric values given in the table. While the numeric values work, the symbolic constants are considerably easier to read, and you should use them instead of the corresponding numbers in any code that you write.

int *flag

  • If this pointer is NULL, then getopt_long() returns the value in the val field of the structure. If it's not NULL, the variable it points to is filled in with the value in val and getopt_long() returns 0. If the flag isn't NULL but the long option is never seen, then the pointed-to variable is not changed.

int val

  • This is the value to return if the long option is seen or to load into *flag if flag is not NULL. Typically, if flag is not NULL, then val is a true/false value, such as 1 or 0. On the other hand, if flag is NULL, then val is usually a character constant. If the long option corresponds to a short one, the character constant should be the same one that appears in the optstring argument for this option. (All of this will become clearer shortly when we see some examples.)

Each long option has a single entry with the values appropriately filled in. The last element in the array should have zeros for all the values. The array need not be sorted; getopt_long() does a linear search. However, sorting it by long name may make it easier for a programmer to read.

The use of flag and val seems confusing at first encounter. Let's step back for a moment and examine why it works the way it does. Most of the time, option processing consists of setting different flag variables when different option letters are seen, like so:

while ((c = getopt(argc, argv, ":af:hv")) != -1) {
    switch (c) {
    case 'a':
        do_all = 1;
        break;
    case 'f':
        myfile = optarg;
        break;
    case 'h':
        do_help = 1;
        break;
    case 'v':
        do_verbose = 1;
        break;
    ...               Error handling code here
   }
   }
   

When flag is not NULL, getopt_long() sets the variable for you. This reduces the three cases in the previous switch to one case. Here is an example long options table and the code to go with it:

int do_all, do_help, do_verbose;    /* flag variables */
char *myfile;

struct option longopts[] = {
   { "all",     no_argument,       & do_all,     1   },
   { "file",    required_argument, NULL,         'f' },
   { "help",    no_argument,       & do_help,    1   },
   { "verbose", no_argument,       & do_verbose, 1   },
   { 0, 0, 0, 0 }
};
...
while ((c = getopt_long(argc, argv, ":f:", longopts, NULL)) != -1) {
    switch (c) {
    case 'f':
        myfile = optarg;
        break;
    case 0:
        /* getopt_long() set a variable, just keep going */
        break;
    ...               Error handling code here
   }
   }
   

Notice that the value passed for the optstring argument no longer contains 'a', 'h', or 'v'. This means that the corresponding short options are not accepted. To allow both long and short options, you would have to restore the corresponding cases from the first example to the switch.

Practically speaking, you should write your programs such that each short option also has a corresponding long option. In this case, it's easiest to have flag be NULL and val be the corresponding single letter.

2.3.3.2 Long Options, POSIX Style

The POSIX standard reserves the -W option for vendor-specific features. Thus, by definition, -W isn't portable across different systems.

If W appears in the optstring argument followed by a semicolon (note: not a colon), then getopt_long() treats -Wlongopt the same as --longopt. Thus, in the previous example, change the call to be:

while ((c = getopt_long(argc, argv, ":f:W;", longopts, NULL)) != -1) {

With this change, -Wall is the same as --all and -Wfile=myfile is the same as --file=myfile. The use of a semicolon makes it possible for a program to use -W as a regular option, if desired. (For example, GCC uses it as a regular option, whereas gawk uses it for POSIX conformance.)

2.3.3.3 getopt_long() Return Value Summary

As should be clear by now, getopt_long() provides a flexible mechanism for option parsing. Table 2.2 summarizes the possible return values and their meaning.

Table 2.2. getopt_long() return values

Return code

Meaning

0

getopt_long() set a flag as found in the long option table.

1

optarg points at a plain command-line argument.

'?'

Invalid option.

':'

Missing option argument.

'x'

Option character 'x'.

–1

End of options.

Finally, we enhance the previous example code, showing the full switch statement:

int do_all, do_help, do_verbose;    /* flag variables */
char *myfile, *user;                /* input file, user name */

struct option longopts[] = {
   { "all",     no_argument,       & do_all,     1   },
   { "file",    required_argument, NULL,         'f' },
   { "help",    no_argument,       & do_help,    1   },
   { "verbose", no_argument,       & do_verbose, 1   },
   { "user"   , optional_argument, NULL,         'u' },
   { 0, 0, 0, 0 }
};
...
while ((c = getopt_long(argc, argv, ":ahvf:u::W;", longopts, NULL)) != –1) {
    switch (c) {
    case 'a':
        do_all = 1;
        break;
    case 'f':
        myfile = optarg;
        break;
    case 'h':
        do_help = 1;
        break;
    case 'u':
        if (optarg != NULL)
           user = optarg;
        else
           user = "root";
        break;
    case 'v':
        do_verbose = 1;
        break;
    case 0:     /* getopt_long() set a variable, just keep going */
        break;
#if 0
    case 1:
        /*
         * Use this case if getopt_long() should go through all
         * arguments. If so, add a leading '-' character to optstring.
         * Actual code, if any, goes here.
         */
        break;
#endif
    case ':':   /* missing option argument */
        fprintf(stderr, "%s: option `-%c' requires an argument\n",
                argv[0], optopt);
        break;
    case '?':
    default:    /* invalid option */
        fprintf(stderr, "%s: option `-%c' is invalid: ignored\n",
                argv[0], optopt);
        break;
    }
}

In your programs, you may wish to have comments for each option letter explaining what each one does. However, if you've used descriptive variable names for each option letter, comments are not as necessary. (Compare do_verbose to vflg.)

2.3.3.4 GNU getopt() or getopt_long() in User Programs

You may wish to use GNU getopt() or getopt_long() in your own programs and have them run on non-Linux systems. That's OK; just copy the source files from a GNU program or from the GNU C Library (GLIBC) CVS archive.3 The source files are getopt.h, getopt.c, and getopt1.c. They are licensed under the GNU Lesser General Public License, which allows library functions to be included even in proprietary programs. You should include a copy of the file COPYING.LIB with your program, along with the files getopt.h, getopt.c, and getopt1.c.

Include the source files in your distribution, and compile them along with any other source files. In your source code that calls getopt_long(), use '#include <getopt.h>', not '#include "getopt.h"'. Then, when compiling, add -I. to the C compiler's command line. That way, the local copy of the header file will be found first.

You may be wondering, "Gee, I already use GNU/Linux. Why should I include getopt_long() in my executable, making it bigger, if the routine is already in the C library?" That's a good question. However, there's nothing to worry about. The source code is set up so that if it's compiled on a system that uses GLIBC, the compiled files will not contain any code! Here's the proof, on our system:

$ uname -a                                   Show system name and type
   Linux example 2.4.18-14 #1 Wed Sep 4 13:35:50 EDT 2002 i686 i686 i386 GNU/Linux
   $ ls -l getopt.o getopt1.o                   Show file sizes
   -rw-r--r--    1 arnold   devel        9836 Mar 24 13:55 getopt.o
   -rw-r--r--    1 arnold   devel       10324 Mar 24 13:55 getopt1.o
   $ size getopt.o getopt1.o                    Show sizes included in executable
   text    data     bss     dec     hex filename
   0       0       0       0       0 getopt.o
   0       0       0       0       0 getopt1.o
   

The size command prints the sizes of the various parts of a binary object or executable file. We explain the output in Section 3.1, "Linux/Unix Address Space," page 52. What's important to understand right now is that, despite the nonzero sizes of the files themselves, they don't contribute anything to the final executable. (We think this is pretty neat.)

  • + Share This
  • 🔖 Save To Your Account

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020