14

I have a program where you enter an option -d and then whether or not you supply a non-optional argument after the option, do something.
Heres my code:

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

#define OPT_LIST "d::" 

int main (int argc, char *argv[])
{
    int c;
    char string[] = "blah";

    while ((c = getopt (argc, argv, OPT_LIST)) != -1)
    {
        switch (c)
        {
            case 'd':
                    printf("%s\n", optarg);
                    break;

            case '?':
                fprintf(stderr, "invalid option\n");
                exit(EXIT_FAILURE);
        }   
    }
}

So if you enter a non-optional argument after the option, it prints the argument. But I want it to print out the char "string" if the user doesn't supply a non-optional argument (this is why I put the double colon in the OPT_LIST). But I'm not sure how to do this so any help would be greatly appreciated.

Heres what happens when I run the program:

user:desktop shaun$ ./arg -d hello
hello
user:desktop shaun$ ./arg -d 
./arg: option requires an argument -- d
invalid option

I'm running a Mac with OS X using C language.

dan1st
  • 12,568
  • 8
  • 34
  • 67
pudumaster
  • 161
  • 1
  • 1
  • 4
  • Um, `printf("%s\n", string)`? –  Oct 26 '13 at 08:23
  • I want it to print string when I run the program like: ./arg -d – pudumaster Oct 26 '13 at 08:26
  • That's exactly what the line above would do if inserted at the place where `fprintf(stderr, "invalid option\n");` currently resides. –  Oct 26 '13 at 08:34
  • I tried that and it printed the string, but it also printed `./arg: option requires an argument -- d` Any idea how to get rid of that error? – pudumaster Oct 26 '13 at 08:38
  • 1
    You can't. `getopt()` is required to print a diagnostic message. –  Oct 26 '13 at 08:52

4 Answers4

20

The "optional value of an option" feature is only a GNU libc extension, not required by POSIX, and is probably simply unimplemented by the libc shipped with Mac OS X.

The options argument is a string that specifies the option characters that are valid for this program. An option character in this string can be followed by a colon (‘:’) to indicate that it takes a required argument. If an option character is followed by two colons (‘::’), its argument is optional; this is a GNU extension.

https://www.gnu.org/software/libc/manual/html_node/Using-Getopt.html

In fact, POSIX.1-2008, section 12.2, "Utility Syntax Guidelines", explicitly forbids this feature:

Guideline 7: Option-arguments should not be optional.

http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_02

Delan Azabani
  • 79,602
  • 28
  • 170
  • 210
14

According to the getopt documentation, it will return : if an option with an argument does not have one. It also sets optopt with the matching argument.

Therefore, use:

int main (int argc, char *argv[])
{
    int c;
    while ((c = getopt (argc, argv, ":d:f:")) != -1)
    {
        switch (c)
        {
            case 'd':
            case 'f':
                printf("option -%c with argument '%s'\n", c, optarg);
                break;
            case ':':
                switch (optopt)
                {
                case 'd':
                    printf("option -%c with default argument value\n", optopt);
                    break;
                default:
                    fprintf(stderr, "option -%c is missing a required argument\n", optopt);
                    return EXIT_FAILURE;
                }
                break;
            case '?':
                fprintf(stderr, "invalid option: -%c\n", optopt);
                return EXIT_FAILURE;
        }
    }
    return EXIT_SUCCESS;
}
Jens Munk
  • 4,627
  • 1
  • 25
  • 40
reece
  • 7,945
  • 1
  • 26
  • 28
  • 2
    You need to use `optstring = ":d:f:"`, with an initial `:`, to allow optional arguments. Something very annoying is that, while `foo -d` and `foo -f arg` work as expected, `foo -d -f arg` mistakes `-f` for the optarg to `-d` .... Any neat workwaround ? – phs Jun 02 '16 at 16:48
  • 2
    @phs, I think you have to manually check if `optarg` looks like an option (e.g. `optarg[0]=='-'`), and if so then rewind optind to read it (`optind-=1;`). This is made trickier if you want to accept a negative number as an optional arg, which may hint at why POSIX [forbids them altogether](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_02) (guideline #7). cf. http://stackoverflow.com/a/32575314/2975337 –  Aug 29 '16 at 03:43
4

Maybe I'm misunderstanding the question. I'm reading it as though the user wants to over ride getopt's default error handling. If that is the case, shouldn't there be a : in the beginning of their OPT_LIST? I think the code above is good, however, I think it will still print

./arg: option requires an argument -- d

To surpress that, don't we need a : at the beginning of the OPT_LIST? For example, changing this:

while ((c = getopt (argc, argv, "d:f:")) != -1)

to this:

while ((c = getopt (argc, argv, ":d:f:")) != -1)

Correct me if I'm wrong.

0

Try this solution. It works for me. Consider option 'z' as option with optional argument.

int c;
opterr = 0; //if (opterr != 0) (which it is by default), getopt() prints its own error messages for invalid options and for missing option arguments.
while ((c = getopt (argc, argv, "x:y:z:")) != -1)
    switch (c)
    {
        case 'x':
        case 'y':
        case 'z':
            printf("OK ... option -%c with argument '%s'\n", c, optarg);
            break;

        case '?':
            if (optopt == 'x' || optopt == 'y')
                fprintf (stderr, "ERR ... Option -%c requires an argument.\n", optopt);
            else if(optopt == 'z' && isprint(optopt))
            {
                printf("OK ... option '-z' without argument \n");
                break;
            }
            else if (isprint (optopt))
                fprintf (stderr, "ERR ... Unknown option `-%c'.\n", optopt);
            else
                fprintf (stderr, "ERR ... Unknown option character `\\x%x'.\n", optopt);
            return -1;
        default: ;
  }

Here are some examples:

./prog -x
    ERR ... Option -x requires an argument.

./prog -y
    ERR ... Option -x requires an argument.

./prog -z
    OK ... option '-z' without argument

./prog -x aaa
    OK ... option -x with argument 'aaa'

./prog -y bbb -x aaa
    OK ... option -y with argument 'bbb'
    OK ... option -x with argument 'aaa'

./prog -x aaa -y bbb -z
    OK ... option -x with argument 'aaa'
    OK ... option -y with argument 'bbb'
    OK ... option '-z' without argument

./prog -x aaa -y bbb -z ccc
    OK ... option -x with argument 'aaa'
    OK ... option -y with argument 'bbb'
    OK ... option -z with argument 'ccc'
Tomas Zubrik
  • 347
  • 3
  • 11