mario::konrad
programming / C++ / sailing / nerd stuff
Tutorial: getopt
© 2002 / Mario Konrad

Einleitung

Praktisch alle Programme nehmen Parameter beim Programmstart entgegen, selbst die grafisch Orientierten (GUIs). In einem früheren Tutorial (Command Line Parameter Parsing) habe ich gezeigt, wie dieses Problem gelöst werden könnte. Dieses Tutorial zeigt nun, wie mit Hilfe der Functionen getopt und getopt_long der Standard C Bibliothek dieses Problem angegangen werden kann.

Dieses Tutorial setzt genügend C Kenntnisse voraus um einfachere Programme zu schreiben, zu übersetzen und auszufüren.

Die Funktionen

Die Standardbibliothek stellt drei Funktionen zur Verfügung um Parameter zu parsen. Dies sind:

Dieses Kapitel geht kurz auf die Funktionen ein und erklärt deren Arbeitsweise, die Parameter und die Verwendung der Funktionen.

getopt

Diese Funktion wird dazu verwendet, Parameter einfacher Struktur wie z.B. -t oder -h zu erkennen. Parameter müssen mit einem - beginnen. Parameter die ohne - beginnen werden nicht als solche erkannt.

Um die Funktion getopt verwenden zu können, muss das Headerfile

#include <unistd.h>

eingefügt werden. Sie hat folgende Signatur:

int getopt(int argc, char * const argv[], const char *optstring)

als globale Variablen sind die folgenden im Einsatz:

extern char *optarg;
extern int optind, opterr, optopt;

Die ersten zwei Parameter sind jene, welche die Funktion main übernimmt.

Der Parameter const char * optstring enthält die möglichen Parameter (je ein Zeichen). Folgt dem Zeichen ein Doppelpunkt :, so erwartet der Parameter ein Argument. Bei zwei Doppelpunkten :: ist das Argument Optional. Beispiele:

Die genaue Spezifikation sind der manpage zu entnehmen: getopt(3).

getopt_long

Im Gegensatz zu getopt welches Parameter in kurzer Form erkennt, ist getopt_long in der Lage Parameter wie --help oder --dir=foobar zu verarbeiten. Es ist zwingend, dass vor dem Parameter zwei Striche stehen --.

Das Headerfile:

#include <getopt.h>

muss dabei eingefügt sein. Die Signatur sieht wie folgt aus:

int getopt_long(int argc, char * const argv[], const char *optstring,
    const struct option *longopts, int *longindex)

Die ersten drei Parameter sind gleich wie bei getopt. Die folgenden Parameter sind zusätzlich und dienen dazu die langen Parameter zu definieren. Die Struktur option (aus getopt.h) sieht im Detail wie folgt aus:

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

Hier eine kurze erklärung. Für eine ausführliche ist die man page zu konsultieren.

Variable Beschreibung
name Name des Parameters, z.B. "help"
has_arg Spezifiziert ob der Parameter ein Argument erwartet. Mögliche Werte: no_argument, required_argument, optinal_argument
flag Spezifiziert wie Resultate zurückgegeben werden sollen. Falls dieser Wert 0 ist, so wird val zurückgeliefert. Falls dieser Wert unterschiedlich von 0 ist, so wird val in die Variable gelegt, zu der flag zeigt (falls der Parameter nicht gefunden wurde, so bleibt die entsprechende Variable unverändert).
val Der Wert, der von getopt_long zurückgegeben werden soll. Hier wird oft die Kurzform des Parameters angegeben, z.B. für help wird h angegeben.

Mit Hilfe dieser Struktur können sehr einfach Parameter definiert werden (Ausschnitt aus getopt2.c):

6
7
8
9
10
11
12
13
14
15
    static const struct option long_options[] =
    {
        { "AAA", no_argument,       0, 'a' },
        { "BBB", no_argument,       0, 'b' },
        { "CCC", required_argument, 0, 'c' },
        { "DDD", no_argument,       0, 'd' },
        { "EEE", no_argument,       0, 0   },
        { "FFF", required_argument, 0, 0   },
        0
    };

Speziell zu beachten gilt es die abschliessende 0. Sie dient dazu getopt_long mitzuteilen, wann die Liste der möglichen Parameter zu Ende ist.

Der letzte Parameter der Funktion dient dazu den Index des gefundenen Parameters aufzunehmen.

Es ist dann sehr einfach die Funktion aufzurufen:

20
21
22
        int result = getopt_long(argc, argv,
            "abc:d",
            long_options, &index);

Diese Verwendungsweise, eine Kombination von optstring und der obigen Struktur, von getopt_long hat den Vorteil, dass sowohl die langen Parameter (--AAA, --BBB, etc.) wie auch die Kurzen (-a, -b, etc.). Die möglichen Parameterformen als Übersicht (ARG ist wieder das Argument):

Kurze Form mit -

-a
-ab
-c ARG
-cARG
-ac ARG
-acARG

Lange Form mit '--'

--AAA
--CCC ARG
--CCC=ARG

Natürlich ist auch eine Kombination von kurzen und langen Parametern möglich. Zu beachten ist jedoch, dass die lange Form immer ein -- und die kurze ein - braucht um als solche identifiziert zu werden. Die kurze Form ist übrigens die Gleiche wie bei getopt.

getopt_long_only

getopt_long_only funktioniert gleich wie getopt_long, jedoch mit dem kleinen Unterschied, dass lange Parameter auch mit - angegeben werden können, anstelle nur mit --.

Die Signatur:

int getopt_long_only(int  argc, char * const argv[], const char * optstring,
    const struct option * longopts, int * longindex)

Da die Ähnlichkeit mit getopt_long so gross ist, wird auf weitere Erklärungen verzichtet.

Beispiele

Dieses Kapitel enthält je ein Beispiel zu den Funktionen getopt und getopt_long.

Weiter unten stehen die Programme als ganzes oder stehen auch bereit zum Download.

getopt

Zuerst die nötigen Headerfiles:

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

Für das Demoprogramm reicht eine Funktion:

int main(int argc, char ** argv)
{

Nun wollen wir parameter parsen, so lange wie Parameter spezifiziert sind. Die Variable optind wird autmatisch von getopt erhöht, so lange wie diese kleiner als die Anzahl spezifierter Parameter argc.

    while (optind < argc)
    {

Alexander Dahl hat hier auf einen Fehler hingewiesen (besten Dank!): die Schleife sollte so aussehen:

    while (1)
    {

damit das Umsortieren von Parametern funktioniert und solche Dinge möglich sind wie:

$ ./getopt1 -a -b -c foobar -d abc def ghi -a

Als Erstes verwenden wir die genannte Funktion um herauszufinden, welcher Parameter angegeben wurde.

        int result = getopt(argc, argv, "abc:d");

Ist das Resultat -1 so ist das Ende er Parameter erreicht, die durch getopt erkannt werden können. Für alle andern Werte wird dann auf der Zeile 10 unterschieden.

        if (result == -1) break; /* end of list */
        switch (result)
        {

Bei einem Resultat von ? handelt es sich um einen unbekannten Parameter.

            case '?': /* unknown parameter */
                break;

Ein : wird geliefert, falls ein Parameter der ein zwingendes Argument erwartet keines erhält.

            case ':': /* missing argument of a parameter */
                fprintf(stderr, "missing argument.\n");
                break;

Die folgenden vier Rückgabewerte entsprechen dem jeweiligen Parameter. Bei c (Zeile 23/24) wird gezeigt, wie auf ein optionales oder zwingendes Argument zugegriffen werden kann.

            case 'a':
                printf("'a' was specified.\n");
                break;
            case 'b':
                printf("'b' was specified.\n");
                break;
            case 'c':
                printf("'c' was specified. Arg: `<%s>`\n", optarg);
                break;
            case 'd':
                printf("'d' was specified.\n");
                break;

Andere Rückgabewerte sind unbekannt.

            default: /* unknown */
                break;
        }
    }

Die obige Schleife (Zeile 6) kann Parameter verarbeiten die unmittelbar hinter dem Programmnamen angegeben werden. Beispiel:

$ ./getopt1 -a -bd

Die folgende Schleife gibt alle folgenden Parameter aus. Achtung: sobald ein Parameter angegeben wird, der nicht in optstring definiert wird, so bricht getopt ab und es werden alle folgenden Parameter in der zweiten Schleife (Zeile 33) ausgegeben. Beispiel:

$ ./getopt1 -a -b foobar  # 'a' und 'b' werden als Parameter erkannt
$ ./getopt1 -a foobar -b  # 'a' wird als Parameter erkannt, 'b' nicht mehr

Nun also die Schleife:

    while (optind < argc)
    {
        printf("other parameter: `<%s>`\n", argv[optind++]);
    }

Das Ende der Funktion hat keinen Bedarf weiterer Erklärungen.

    return 0;
}

getopt_long

Zuerst wieder die nötigen Headerfiles:

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

Wie gehabt: für dieses kleine Tutorial genügt uns eine Funktion.

int main(int argc, char ** argv)
{

Nun die Definition der langen Parameter. Die Struktur wurde ja schon in Kapitel 2.2 erklärt.

    static const struct option long_options[] =
    {
        { "AAA", no_argument,       0, 'a' },
        { "BBB", no_argument,       0, 'b' },
        { "CCC", required_argument, 0, 'c' },
        { "DDD", no_argument,       0, 'd' },
        { "EEE", no_argument,       0, 0   },
        { "FFF", required_argument, 0, 0   },
        0
    };

Wir benötigen wiederum eine Schleife, die solange auf Parameter testet wie auf der Kommandozeile angegeben wurde (maximal):

    while (1)
    {

Hier auch die Endlosschleife wie oben beschrieben.

Ein paar lokale Variablen erleichtern uns das Leben.

        int index = -1;
        struct option * opt = 0;

Schon gehts ans Eingemachte. Wie in Kapitel 2.2 erwähnt bedienen wir uns einer Kombination von optstring und der Struktur option um lange und kurze Parameter zuzulassen.

        int result = getopt_long(argc, argv,
            "abc:d",
            long_options, &index);

Das Ende der Liste ist erreicht, also die while-Schleife (Zeile 16) verlassen.

        if (result == -1) break; /* end of list */

Die Auswertung des Resultats präsentiert sich analog zu der bei getopt:

        switch (result)
        {

Dieser Fall kann aus zwei Gründen auftreten: es wurde entweder die kurze Form angegeben (-a) oder es wurde die lange Form angegeben (--AAA). Dies wird ermöglicht durch die Definition des langen Parameters (Zeile 8) oder durch den optstring (Zeile 21). Für die nächsten drei Parameter gilt das Selbe.

            case 'a': /* same as index==0 */
                printf("'a'/'AAA' was specified.\n");
                break;
            case 'b': /* same as index==1 */
                printf("'b'/'BBB' was specified.\n");
                break;
            case 'c': /* same as index==2 */
                printf("'c'/'CCC' was specified. Arg: `<%s>`\n",
                    optarg);
                break;
            case 'd': /* same as index==3 */
                printf("'d'/'DDD' was specified.\n");
                break;

Dies ist ein Spezialfall: die Parameter EEE und FFF besitzen keine Kurzform. In der obigen Struktur (Zeilen 12/13) haben wir angegeben, dass als result der Wert 0 geliefert werden soll falls einer dieser Parameter angegeben wurde. Natürlich hätten wir auch eindeutige Rückgabewerte definieren können.

In den folgenden Zeilen wird gezeicht, wie mit Hilfe der Variable index (gesetzt von getopt_long) auf die entsprechende Parameterdefinition zugegriffen werden kann.

            case 0: /* all parameter that do not */
                    /* appear in the optstring */
                opt = (struct option *)&(long_options[index]);
                printf("'%s' was specified.",
                    opt->name);
                if (opt->has_arg == required_argument)
                    printf("Arg: `<%s>`", optarg);
                printf("\n");
                break;

Auf den Rest des Programms wird nicht weiter eingegangen, da dies schon in Kapitel 3.1 erklärt wurde.

                default: /* unknown */
                    break;
            }
        }
        /* print all other parameters */
        while (optind < argc)
        {
            printf("other parameter: `<%s>`\n", argv[optind++]);
        }
        return 0;
    }

Die Programme

Dieses Kapitel enhält nochmals die vollständigen Programme ohne die eingestreuten Kommentare.

getopt1.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <stdio.h>
#include <unistd.h>

int main(int argc, char ** argv)
{
    while (1)
    {
        int result = getopt(argc, argv, "abc:d");
        if (result == -1) break; /* end of list */
        switch (result)
        {
            case '?': /* unknown parameter */
                break;
            case ':': /* missing argument of a parameter */
                fprintf(stderr, "missing argument.\n");
                break;
            case 'a':
                printf("'a' was specified.\n");
                break;
            case 'b':
                printf("'b' was specified.\n");
                break;
            case 'c':
                printf("'c' was specified. Arg: <%s>\n", optarg);
                break;
            case 'd':
                printf("'d' was specified.\n");
                break;
            default: /* unknown */
                break;
        }
    }
    while (optind < argc)
    {
        printf("other parameter: <%s>\n", argv[optind++]);
    }
    return 0;
}

getopt2.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <stdio.h>
#include <getopt.h>

int main(int argc, char ** argv)
{
    static const struct option long_options[] =
    {
        { "AAA", no_argument,       0, 'a' },
        { "BBB", no_argument,       0, 'b' },
        { "CCC", required_argument, 0, 'c' },
        { "DDD", no_argument,       0, 'd' },
        { "EEE", no_argument,       0, 0   },
        { "FFF", required_argument, 0, 0   },
        0
    };
    while (1)
    {
        int index = -1;
        struct option * opt = 0;
        int result = getopt_long(argc, argv,
            "abc:d",
            long_options, &index);
        if (result == -1) break; /* end of list */
        switch (result)
        {
            case 'a': /* same as index==0 */
                printf("'a'/'AAA' was specified.\n");
                break;
            case 'b': /* same as index==1 */
                printf("'b'/'BBB' was specified.\n");
                break;
            case 'c': /* same as index==2 */
                printf("'c'/'CCC' was specified. Arg: <%s>\n",
                    optarg);
                break;
            case 'd': /* same as index==3 */
                printf("'d'/'DDD' was specified.\n");
                break;
            case 0: /* all parameter that do not */
                    /* appear in the optstring */
                opt = (struct option *)&(long_options[index]);
                printf("'%s' was specified.",
                    opt->name);
                if (opt->has_arg == required_argument)
                    printf("Arg: <%s>", optarg);
                printf("\n");
                break;
            default: /* unknown */
                break;
        }
    }
    /* print all other parameters */
    while (optind < argc)
    {
        printf("other parameter: <%s>\n", argv[optind++]);
    }
    return 0;
}

Download

Alle source code Files die auf dieser Seite publiziert werden, sind frei zu kopieren, modifizieren, weiterverbreiten und zu verwenden.

Das Copyright des Tutorials liegt bei Mario Konrad.