getopt analysiert keine optionalen Argumente für Parameter

Lesezeit: 6 Minuten

Benutzeravatar von hayalci
hayalci

In C analysiert getopt_long nicht die optionalen Argumente für Befehlszeilenparameter parameter.

Wenn ich das Programm ausführe, wird das optionale Argument nicht erkannt, wie im folgenden Beispiel ausgeführt.

$ ./respond --praise John
Kudos to John
$ ./respond --blame John
You suck !
$ ./respond --blame
You suck !

Hier ist der Testcode.

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

int main(int argc, char ** argv )
{
    int getopt_ret, option_index;
    static struct option long_options[] = {
               {"praise",  required_argument, 0, 'p'},
               {"blame",  optional_argument, 0, 'b'},
               {0, 0, 0, 0}       };
    while (1) {
        getopt_ret = getopt_long( argc, argv, "p:b::",
                                  long_options,  &option_index);
        if (getopt_ret == -1) break;

        switch(getopt_ret)
        {
            case 0: break;
            case 'p':
                printf("Kudos to %s\n", optarg); break;
            case 'b':
                printf("You suck ");
                if (optarg)
                    printf (", %s!\n", optarg);
                else
                    printf ("!\n", optarg);
                break;
            case '?':
                printf("Unknown option\n"); break;
        }
    } 
    return 0;
}

  • Ich dokumentiere dies hier mit der Antwort, damit andere Leute nicht mit dem Kopf gegen die Wand schlagen müssen.

    – hayalci

    27. Juni 2009 um 12:38 Uhr

Benutzeravatar von hayalci
hayalci

Obwohl in der glibc-Dokumentation oder der getopt-Manpage nicht erwähnt, erfordern optionale Argumente für lange Befehlszeilenparameter das Gleichheitszeichen (=). Das Leerzeichen, das das optionale Argument vom Parameter trennt, funktioniert nicht.

Ein Beispiellauf mit dem Testcode:

$ ./respond --praise John
Kudos to John
$ ./respond --praise=John
Kudos to John
$ ./respond --blame John
You suck !
$ ./respond --blame=John
You suck , John!

  • Beachten Sie, dass Perls Getopt::Long-Modul NICHT die gleiche Anforderung hat. Boost.Program_options tut es.

    – Rob Kennedy

    27. Juni 2009 um 15:08 Uhr

  • Wow, das ist scheiße. Ich bin auf das gleiche Problem mit regulärem getopt() gestoßen und wenn ich einen Optstring “a::” verwende, würde optarg nur gesetzt, wenn Sie NULL Leerzeichen zwischen der Option und dem Argument haben, wie z. B. ‘-afoo’

    – SiegeX

    20. November 2009 um 23:44 Uhr

  • Zumindest für meine kurzen Optionen wie -a oder -a=300ich musste ein hinzufügen if (optarg[0] == '=') {memmove(optarg, optarg+1, strlen(optarg));} die abzustreifen =. Sonst hätte ich es immer gehabt =300 in optarg. Oder ich hätte es so nennen müssen: -a300 – was ziemlich hässlich ist, finde ich. Danke trotzdem für deine Antwort, hat mir sehr geholfen!

    – mozzbozz

    31. Mai 2014 um 17:27 Uhr


  • In diesem Fall ist es besser, keine optionalen Argumente zu verwenden. Ist das ein Bug oder ein Feature? Jetzt habe ich Lust dazu ./respond --blame=glibc.

    – Petersohn

    7. März 2015 um 19:11 Uhr

  • Ich wollte ablehnen, habe dann aber festgestellt, dass Ihre Antwort großartig ist. Es ist nur das Verhalten, das ich ablehnen möchte. Vielen Dank!

    – lmat – Wiedereinsetzung von Monica

    17. Mai 2019 um 15:58 Uhr

Benutzeravatar von Brian Vandenberg
Brian Vandenberg

Die Manpage dokumentiert es sicherlich nicht sehr gut, aber der Quellcode hilft ein wenig.

Kurz gesagt: Sie sollten so etwas wie das Folgende tun (obwohl dies vielleicht etwas übertrieben ist):

if(   !optarg
   && optind < argc // make sure optind is valid
   && NULL != argv[optind] // make sure it's not a null string
   && '\0' != argv[optind][0] // ... or an empty string
   && '-' != argv[optind][0] // ... or another option
  ) {
  // update optind so the next getopt_long invocation skips argv[optind]
  my_optarg = argv[optind++];
}
/* ... */

Aus den Kommentaren vor _getopt_internal:

Wenn getopt findet ein anderes Optionszeichen, gibt es dieses Zeichen zurück,
Aktualisierung optind und nextchar damit der nächste Anruf an getopt kann den Scan mit dem folgenden Optionszeichen oder ARGV-Element fortsetzen.

Wenn keine Optionszeichen mehr vorhanden sind, getopt gibt -1 zurück. Dann optind ist der Index in ARGV des ersten ARGV-Elements, das keine Option ist. (Die ARGV-Elemente wurden permutiert, sodass diejenigen, die keine Optionen sind, jetzt an letzter Stelle stehen.) <-- a note from me:
if the 3rd argument to getopt_long starts with a dash, argv will not
be permuted

Wenn auf ein Zeichen in OPTSTRING ein Doppelpunkt folgt, bedeutet dies, dass ein Argument gewünscht wird, sodass der folgende Text im selben ARGV-Element oder der Text des folgenden ARGV-Elements zurückgegeben wird optarg. Zwei Doppelpunkte bedeuten eine Option, die ein optionales Argument verlangt; wenn es Text im aktuellen ARGV-Element gibt, wird es zurückgegeben optarg, Andernfalls optarg wird auf Null gesetzt.

…wobei man zwischen den Zeilen lesen muss. Folgendes macht was du willst:

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

int main(int argc, char* argv[] ) {
  int getopt_ret;
  int option_index;
  static struct option long_options[] = {
      {"praise",  required_argument, 0, 'p'}
    , {"blame",  optional_argument, 0, 'b'}
    , {0, 0, 0, 0}
  };

  while( -1 != ( getopt_ret = getopt_long(  argc
                                          , argv
                                          , "p:b::"
                                          , long_options
                                          , &option_index) ) ) {
    const char *tmp_optarg = optarg;
    switch( getopt_ret ) {
      case 0: break;
      case 1:
        // handle non-option arguments here if you put a `-`
        // at the beginning of getopt_long's 3rd argument
        break;
      case 'p':
        printf("Kudos to %s\n", optarg); break;
      case 'b':
        if(   !optarg
           && NULL != argv[optind]
           && '-' != argv[optind][0] ) {
          // This is what makes it work; if `optarg` isn't set
          // and argv[optind] doesn't look like another option,
          // then assume it's our parameter and overtly modify optind
          // to compensate.
          //
          // I'm not terribly fond of how this is done in the getopt
          // API, but if you look at the man page it documents the
          // existence of `optarg`, `optind`, etc, and they're
          // not marked const -- implying they expect and intend you
          // to modify them if needed.
          tmp_optarg = argv[optind++];
        }
        printf( "You suck" );
        if (tmp_optarg) {
          printf (", %s!\n", tmp_optarg);
        } else {
          printf ("!\n");
        }
        break;
      case '?':
        printf("Unknown option\n");
        break;
      default:
        printf( "Unknown: getopt_ret == %d\n", getopt_ret );
        break;
    }
  }
  return 0;
}

  • Das hat wirklich gut funktioniert, danke. Nicht sicher, woher Sie optindex haben; es heißt (extern int) optind für mich.

    – panloco

    24. Dezember 2017 um 22:21 Uhr

  • Im zweiten Codebeispiel ist ein Fehler, das sollte es sein optind Anstatt von optindex.

    – Sebastian Schrader

    14. Januar 2018 um 2:18 Uhr


  • Sieht so aus, als ob der Code optindex beibehalten sollte. Andernfalls zeigt es immer auf 0. wir müssen optindex für jede Option vorantreiben.

    – Howard Shane

    16. April 2018 um 19:03 Uhr

Benutzeravatar von larsewi
Larsewi

Ich bin kürzlich selbst auf dieses Problem gestoßen. Ich kam zu einer ähnlichen Lösung wie die von Brian Vandenberg und Haystack vorgeschlagene. Aber um die Lesbarkeit zu verbessern und Codeduplizierung zu vermeiden, können Sie alles in einem Makro wie dem folgenden zusammenfassen:

#define OPTIONAL_ARGUMENT_IS_PRESENT \
    ((optarg == NULL && optind < argc && argv[optind][0] != '-') \
     ? (bool) (optarg = argv[optind++]) \
     : (optarg != NULL))

Das Makro kann wie folgt verwendet werden:

case 'o': // option with optional argument
    if (OPTIONAL_ARGUMENT_IS_PRESENT)
    {
        // Handle is present
    }
    else
    {
        // Handle is not present
    }
    break;

Wenn Sie interessiert sind, können Sie in einem von mir verfassten Blog-Beitrag mehr darüber lesen, wie diese Lösung funktioniert:
https://cfengine.com/blog/2021/optional-arguments-with-getopt-long/

Diese Lösung ist getestet und wird – zum Zeitpunkt dieses Schreibens – derzeit in CFEngine verwendet.

Ich bin auch auf das gleiche Problem gestoßen und bin hierher gekommen. Dann wurde mir das klar. Sie haben keinen großen Anwendungsfall von “optional_argument” . Wenn eine Option erforderlich ist, überprüfen Sie die Programmlogik. Wenn eine Option optional ist, müssen Sie nichts tun, da auf der Ebene getopt alle Optionen optional sind, sie sind nicht obligatorisch, sodass es keinen Anwendungsfall für “optional_argument” gibt. Hoffe das hilft.

PS: Für das obige Beispiel denke ich, dass die richtigen Optionen –praise –praise-name “name” –blame –blame-name “name” sind

1411260cookie-checkgetopt analysiert keine optionalen Argumente für Parameter

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy